Note: Toggle the “code” button to view/remove R code

This example highlights my data wrangling and visualization skills in an exploratory analysis of racial/ethnic inequity, poverty, and academic performance in public schools in Texas. This was a pet project that I created to satisfy my own curiosity on the topic and to get to know the publicly available data related to education in Texas. This page provides the steps required to go from several raw, public data sets to produce a series of interactive data visualizations that explore relationships between race/ethnicity, poverty, and educational outcomes (like these), as well as how those relationships are moderated by regional private school attendance.

Campus-Level Data for 2019:

Load Packages

You can toggle the “code” button to the right to see this chunk. You can hide long code chunks by toggling the same button.

library(tidyverse)
library(readxl)
library(plotly)
library(ggpubr)
library(gapminder)
library(kableExtra)
library(summarytools)

Percent of Students Meeting Grade-Level by Race/Ethnic Categories

For educational outcomes, I use the publicly available Texas Academic Performance Report (TAPR) data for the year 2019. This is a very large data set that that I have subsetted to focus on the percentage of students in three racial/ethnic categories (variable: Student Group) who meet the expectations of their respective grade levels (variable: Meets Grade Level (%)). I also include the numeric campus identifier cid and the respective denominators for each percentile value (variable: Size of Student Group at School), which informs the absolute number of students from each category at the school. I use this value to weight the observations in the Loess smoothing function and to inform the size parameter in the plots. For the original variable names, see the TAPR Codebook.

As described in the codebook for the data set, student groups’ percentile values with very small denominators are masked (coded as negative placeholder values of “-1”) in order to preclude the possibility of identification of individuals in the data. I exclude these masked values from the data set, as well as any school that reports missing data for all three student groups. Denominators of the second smallest student group for each school are also masked with a unique placeholder (“-3”) when the smallest group requires masking. I replace this set of masked values with a reasonable proxy of half the size of the next largest group in the data.

This code does the following:

  1. Read in subset of TAPR Data
  2. Filter schools with all missing values
  3. Pivot the data to include (up to) three observations per school, corresponding to the three percentile scores Meets Grade Level (%) of students in each racial/ethnic category that meet the grade-level expectations.
  4. Create category names for student groups: Student Group and Size of Student Group at School
  5. Remove masked data for percentiles and recode the masked denominator Size of Student Group at School with the proxy.
  6. Create an additional variable ln_meets_denom of logged group size for use as weight in the Loess smoothing algorithm.
# Load TAPR data; rename vars
campus_2019 <- read.csv("docs/TAPR_2019_subset.csv") %>%
  transmute(
    cid = CAMPUS,
    `African American` = CDB00A001219R,
    `Hispanic` = CDH00A001219R,
    `White` = CDW00A001219R,
    aa_total = CDB00A001019D,
    h_total = CDH00A001019D,
    w_total = CDW00A001019D) %>%
  # Filter out schools with no data (~ 10% of campuses)
  filter(!is.na(`African American`) | !is.na(`Hispanic`) | !is.na(`White`))
# Pivot values and descriptive categories
data <- cbind(
  pivot_longer(campus_2019 %>% select(1:4), cols = 2:4, names_to = "Student Group", values_to = "Meets Grade Level (%)"),
  pivot_longer(campus_2019 %>% select(5:7), cols = 1:3, names_to = "group_denom", values_to = "Size of Student Group at School")) %>%
  group_by(cid) %>%
  mutate(
    `Meets Grade Level (%)` = ifelse(`Meets Grade Level (%)` == -1, NA, `Meets Grade Level (%)`), # rate = -1 is masked data and unusable
    `Size of Student Group at School` = ifelse(`Size of Student Group at School` == -1, NA, # denominator = -1 is masked data and unusable
                     `Size of Student Group at School`), 
    `Size of Student Group at School` = ifelse(`Size of Student Group at School` == -3, # denominator = -3 is second smallest group
                     nth(`Size of Student Group at School`, 2, order_by = `Size of Student Group at School`) / 2, 
                     `Size of Student Group at School`), # recover with reasonable proxy of half largest group
    ln_meets_denom = log(`Size of Student Group at School`)) %>% # log will be better for size parameter
  ungroup()
# Show Data
data[1:9,] %>%
  kbl(caption = "Structure of the Data Set: Showing the First Three Schools") %>%
  kable_paper("hover", full_width = F)
Structure of the Data Set: Showing the First Three Schools
cid Student Group Meets Grade Level (%) group_denom Size of Student Group at School ln_meets_denom
1902001 African American 71 aa_total 7 1.945910
1902001 Hispanic 58 h_total 19 2.944439
1902001 White 67 w_total 180 5.192957
1902041 African American 0 aa_total 5 1.609438
1902041 Hispanic 48 h_total 31 3.433987
1902041 White 60 w_total 282 5.641907
1902103 African American 50 aa_total 6 1.791759
1902103 Hispanic 75 h_total 12 2.484907
1902103 White 62 w_total 330 5.799093

Campus-Level Poverty Data

For campus-level poverty statistics, I use the public data available at the Texas Education Agency’s (TEA) school data visualizer, which includes an extensive set of campus-level attributes. I am primarily interested in the percentage of students from economically disadvantaged backgrounds, measured as the percentage of students “…Students eligible for free or reduced-price lunch or other public assistance as reported on the PEIMS October snapshot” (see definitions). Unfortunately, the source does not reveal precisely which year this data comes from! School-level poverty is a relatively stable attribute, though, so this is not a huge concern.

The challenge is that this data does not include the campus ID as a stand-alone variable. I must first extract it from a longer string to create a comparable cid variable that can be used to join this data set to the first one. Because there is no universal pattern to parse this string, I have to split on three different patterns and extract the rightmost element in order to recover all the campus identifiers. This is because some schools have more than one set of the ” (” pattern on the right-hand side of the “||” pattern. This is complicated; however, the end result is the extraction of the cid variable for each school, which I validated to ensure that all schools are accounted for.

school <- read_excel("docs/school.xlsx")
school <- school %>%
  transmute(
    Campus = `Campus or District`, 
    `Eco. Disadvantaged (%)` = `% Eco Disadvantaged`,
    `Campus Type` = factor(`Entity Type`),
    enrollment_public = Enrollment) %>%
  mutate(
    n = str_split(Campus, " \\|\\|", simplify = TRUE)[,2], # First split on "||"
    nn = str_split(n, " \\(", simplify = TRUE)[,2], # Next split on " ("
    nnn = str_split(n, " \\(", simplify = TRUE)[,3], # A few have an additional " ("
    cid = as.numeric(gsub("\\D+", "", nn)),
    cid = ifelse(is.na(cid), as.numeric(gsub("\\D+", "", nnn)), cid)) %>%
  select(-starts_with("n"))

school[1:5,] %>%
  kbl(caption = "Structure of the Poverty Data after Recovering the Campus Identifier") %>%
  kable_paper("hover", full_width = F)
Structure of the Poverty Data after Recovering the Campus Identifier
Campus Eco. Disadvantaged (%) Campus Type enrollment_public cid
CAYUGA H S || CAYUGA ISD (001902001) 34.9 High School 166 1902001
CAYUGA MIDDLE || CAYUGA ISD (001902041) 40.6 Middle & Jr. High School 133 1902041
CAYUGA EL || CAYUGA ISD (001902103) 38.1 Elementary School 236 1902103
ELKHART H S || ELKHART ISD (001903001) 47.4 High School 344 1903001
ELKHART MIDDLE || ELKHART ISD (001903041) 45.1 Middle & Jr. High School 268 1903041

Codes: School, District, Region, County

This data set from TEA includes the codes that can be used for geographic identification in the plots. I use the region code to disaggregate the full data set into manageable subsets and to join this data with the private schools data in the final set of analyses.

codes <- read_csv("docs/school and district.csv")
codes <- codes %>%
  transmute(
    cid = as.numeric(gsub("[^[:alnum:] ]", "", `School Number`)),
    district = as.numeric(gsub("[^[:alnum:] ]", "", `District Number`)),
    county = as.numeric(gsub("[^[:alnum:] ]", "", `County Number`)),
    region = as.numeric(gsub("[^[:alnum:] ]", "", `ESC Region Served`)))

Join Data and See Descriptive Statistics

Taking the TAPR 2019 data as the base set, I match approximately 98.3% of campuses with the TEA school-level data by their campus id (cid). For parsimony, I exclude those %1.7% campuses that do not exist in both data sets. We can see that the masking and missing data results in approximately 10% of missing data from the TAPR 2019 data; however, due to the logic of how the masking is coded, those masked observations are likely to be smaller, relatively more homogeneous schools, otherwise, they would not have very small absolute numbers of students from any of the three racial/ethnic backgrounds.

campus <- left_join(school, codes, by = "cid") %>% right_join(data, by = "cid") %>%
  filter(!is.na(Campus))
print(
  dfSummary(campus,
          plain.ascii  = FALSE, 
          style        = "grid", 
          graph.magnif = 0.75, 
          valid.col    = FALSE,
          tmp.img.dir  = "/tmp",
          silent = TRUE),
  method = "render")

Data Frame Summary

Dimensions: 23658 x 13
Duplicates: 0
No Variable Stats / Values Freqs (% of Valid) Graph Missing
1 Campus [character]
1. 3D ACADEMY || DONNA ISD (
2. A & M CONS H S || COLLEGE
3. A & M CONSOLIDATED MIDDLE
4. A B DUNCAN COLLEGIATE EL
5. A C BLUNT MIDDLE || ARANS
6. A C JONES H S || BEEVILLE
7. A C JONES HEALTH PROFESSI
8. A E BUTLER INT || QUINLAN
9. A G ELDER EL || JOSHUA IS
10. A LEAL JR MIDDLE || HARLA
[ 7876 others ]
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
3(0.0%)
23628(99.9%)
0 (0.0%)
2 Eco. Disadvantaged (%) [numeric]
Mean (sd) : 63.5 (26.1)
min ≤ med ≤ max:
0 ≤ 67.5 ≤ 100
IQR (CV) : 42.1 (0.4)
982 distinct values 0 (0.0%)
3 Campus Type [factor]
1. Elementary School
2. High School
3. K-12 Schools
4. Middle & Jr. High School
12963(54.8%)
4539(19.2%)
1284(5.4%)
4872(20.6%)
0 (0.0%)
4 enrollment_public [numeric]
Mean (sd) : 646.5 (538.7)
min ≤ med ≤ max:
1 ≤ 526.5 ≤ 5328
IQR (CV) : 395 (0.8)
1728 distinct values 0 (0.0%)
5 cid [numeric]
Mean (sd) : 115822704 (72639619)
min ≤ med ≤ max:
1902001 ≤ 101912278 ≤ 254902101
IQR (CV) : 120998818 (0.6)
7886 distinct values 0 (0.0%)
6 district [numeric]
Mean (sd) : 115857 (72676.9)
min ≤ med ≤ max:
1902 ≤ 101912 ≤ 254902
IQR (CV) : 120999 (0.6)
1193 distinct values 144 (0.6%)
7 county [numeric]
Mean (sd) : 115 (72.7)
min ≤ med ≤ max:
1 ≤ 101 ≤ 254
IQR (CV) : 121 (0.6)
253 distinct values 144 (0.6%)
8 region [numeric]
Mean (sd) : 9.7 (5.7)
min ≤ med ≤ max:
1 ≤ 10 ≤ 20
IQR (CV) : 9 (0.6)
20 distinct values 144 (0.6%)
9 Student Group [character]
1. African American
2. Hispanic
3. White
7886(33.3%)
7886(33.3%)
7886(33.3%)
0 (0.0%)
10 Meets Grade Level (%) [integer]
Mean (sd) : 46.2 (18.7)
min ≤ med ≤ max:
0 ≤ 45 ≤ 100
IQR (CV) : 25 (0.4)
100 distinct values 2392 (10.1%)
11 group_denom [character]
1. aa_total
2. h_total
3. w_total
7886(33.3%)
7886(33.3%)
7886(33.3%)
0 (0.0%)
12 Size of Student Group at School [numeric]
Mean (sd) : 376.6 (513.2)
min ≤ med ≤ max:
2.5 ≤ 195 ≤ 5395
IQR (CV) : 411 (1.4)
2329 distinct values 2539 (10.7%)
13 ln_meets_denom [numeric]
Mean (sd) : 5.1 (1.4)
min ≤ med ≤ max:
0.9 ≤ 5.3 ≤ 8.6
IQR (CV) : 2 (0.3)
2329 distinct values 2539 (10.7%)

Generated by summarytools 1.0.1 (R version 4.0.3)
2022-08-02

Exploring the Relationship between Race/Ethnicity, Poverty, and Education Outcomes

Here I write a function to build plotly interactive visualizations. Readers can hover over the image to get point-level data, as well as to zoom in to a plot region and toggle a Student Group on/off in the point/color legend. School-level data is plotted on the x-axis based on poverty levels Eco. Disadvanted (%). School-level data disaggregated by race/ethnicity on the y-axis to show each racial/ethnic group’s academic performance as Meets Grade Level (%). The three race/ethnic groups are connected by vertical lines, showing the distance or spread (y-axis) in performance among the groups within each campus. The size of each point on the plot is mapped by the absolute number of students in each race/ethnicity category within that school. The lines are generated through Loess smoothing algorithms that display (non-linear) trends across the x-axis.

Create Functions for Interactive Plots

I write a custom function that takes the data and two filtering parameters to slice the full data set: campus %>% filter('Campus Type' == {{type}} & region == {{r}}). I pass this function to lapply() to generate plots for each region and Campus Type (excluding K-12 schools for parsimony).

# Regions 1 to 20
r <- 1:20
# Blank list for full sample objects
fs <- list()
# Create function to generate 20 plots
plots <- function(data, r, type) {

t <-  ggplot(data = campus %>% filter(`Campus Type` == {{type}} & region == {{r}}), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle(paste("Region", {{r}}, {{type}}, "Performance by Race/Ethnicity and Poverty", sep = " "))
  
t[[r]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

#print(t[[r]])
#return(t[[r]])
}

# Stores high school plots for 20 regions in indexed list
gg_hs <- lapply(r, plots, data = campus, type = "High School")

# Stores high school plots for 20 regions in indexed list
gg_jhs <- lapply(r, plots, data = campus, type = "Middle & Jr. High School")

# Stores elementary school plots for 20 regions in indexed list
gg_es <- lapply(r, plots, data = campus, type = "Elementary School")

# Full sample: High school
t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("Full Sample of High Schools: Performance by Race/Ethnicity and Poverty")
fs[[1]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

# Full sample: Jr. High school
t <-  ggplot(data = campus %>% filter(`Campus Type` == "Middle & Jr. High School"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("Full Sample of Middle Schools: Performance by Race/Ethnicity and Poverty")
fs[[2]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

# Full sample: Elementary school
t <-  ggplot(data = campus %>% filter(`Campus Type` == "Elementary School"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("Full Sample of Elementary Schools: Performance by Race/Ethnicity and Poverty")
fs[[3]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

Plots: Race/Ethnicity, Poverty, and Education Outcomes

The functions have stored three sets of 20 plots within indexed lists - one plot for each Texas Education Service Center Region at each campus level. You can view all 60 plots here. You can see a map of the Texas ESC Regions here.

Below are the plots for the largest (Region 4: Houston area) and a mid-sized (Region 19: El Paso area) subset. These exploratory plots demonstrate the strong relationship between poverty, race/ethnicity, and educational outcomes. Between schools, and generally within schools, white students tend to have higher rates of meeting grade-level expectations. The full sample plots reveal that race has a persistent difference level (or growth intercept) regardless of the age group of the students. However the trend across poverty levels (or (negative) growth slope) is increasingly non-linear as students age into high school. This is evident in the increasingly steep Loess curves on the extremities of the x-axis for high school campuses, compared with other campuses, as well as junior high campuses compared with elementary campuses. This analysis is exploratory and does not make causal claims. It is important to remember that I have not made sufficient efforts at eliminating endogeneity to make causal claims. One clear takeaway, though, is that further work would need to recognize this non-linearity in any analysis using this data. Otherwise, relationships among these factors might be overlooked.

One salient fact that emerges from this data is the persistent and pervasive inequality in performance among students with differing racial/ethnic backgrounds. It is important to keep in mind that an important source of endogeneity remains in this data, due to the fact that Eco. Disadvantaged (%) is a campus-level variable and not a Student Group variable. Within a given campus, if the white students are less likely to be among those students who are economically disadvantaged, the campus-level poverty loses the ability to explain these outcomes. With access to student-level data within campuses, this analysis could go much further. Regardless, it is clear that the public education system is unable to compensate for the historical disadvantages faced by racial/ethnic minorities, as is a well known problem across the United States.

# Houston: Region 4
htmltools::tagList(list(gg_hs[[4]], gg_jhs[[4]], gg_es[[4]]))
# El Paso: Region 19
htmltools::tagList(list(gg_hs[[19]], gg_jhs[[19]], gg_es[[19]]))
# Full Samples
htmltools::tagList(list(fs[[1]], fs[[2]], fs[[3]]))

Moderation of Relationships by Private School Attendance Rates

Private Schools Data 2018 - 2019

How does private school attendance moderate the relationship between race/ethnicity, poverty levels, and educational outcomes? I downloaded the 2019 private school enrollment data from the Texas Private School Accreditation Commission site. I grouped the data by region and combined it with the public school-level data from the Texas Education Agency’s data used above. I want to explore whether different patterns emerge in these relationships when the data is subset to include small regions with low and high rates of private student attendance.

# Load 2019 private school data and Filter out pre-K and closed schools
private <-  read_excel("docs/private schools 2018-2019.xlsx")
private <-private %>%
  filter(!`Grade High` %in% c("Pre-K", "Early Education") & Closed == FALSE) %>%
  transmute(
    district = as.numeric(`District Number`),
    county = as.numeric(`County Number`),
    region = as.numeric(`Region Name`),
    enrollment_private = as.numeric(Enrollment))
# Aggregate enrollment by region
private_region <- private %>%
  group_by(region) %>%
  summarise(
    private_enrollment_region = sum(enrollment_private, na.rm = TRUE)) %>%
  ungroup()
# Aggregate public school enrollment by region
public_region <- school %>%
  left_join(codes, by = "cid") %>%
  group_by(region) %>%
  summarise(
    public_enrollment_region = sum(enrollment_public, na.rm = TRUE)) %>%
  ungroup()
#Join and create variables and factor labels
region <- left_join(private_region, public_region, by = "region") 
region <- region %>%
  mutate(
    `Total Enrollment` = private_enrollment_region + public_enrollment_region,
    `Private Students in Region (%)` = 100 * ( private_enrollment_region / `Total Enrollment`),
    Region_lab = paste("Region", region, sep = " "),
    Region = factor(region, ordered = TRUE, labels = Region_lab),
    `Private School Rates` = ifelse(
      region %in% c(3, 18, 2, 19, 6), "High and Small Pop.", ifelse(
        region %in% c(12, 15, 14, 8, 16), "Low and Small Pop.", "Middle or Large Pop."))) %>%
  arrange(`Private Students in Region (%)`) %>%
  mutate(
    Region_lab = paste("Region", region, sep = " "),
    `Region Ordered` = factor(region, levels = region, 
                              labels = paste("Region", region, sep = " ")))

Visualizing Public and Private Schools

In order to make the comparison more reasonable, I focus on ESC regions with similar Total Enrollment that have low and high rates of private school attendance in the region. Thus, the groups of schools I compare later come from the red regions (small regions with rates of private school attendance) and the green regions (small regions with low rates of private school attendance). I should note that this effort to choose comparable groups of regions does not sufficiently eliminate endogeneity to consider any outcomes causal. This is an exploratory analysis.

# Scatter and bar plots
s <- ggplot(region, 
            aes(x = log(`Total Enrollment`), 
                y = `Private Students in Region (%)`, 
                label = region, 
                color = `Private School Rates`)) +
  geom_text(size = 5) +
  guides(color = FALSE) 
b <- ggplot(region, 
            aes(x = `Private Students in Region (%)`, 
                y = `Region Ordered`, 
                fill = `Private School Rates`)) +
  geom_col() +
  theme(legend.position = "bottom")
ggarrange(s, b, nrow = 1, common.legend = TRUE, legend = "bottom")

Education Outcomes by Low and High Rates of Regional Private School Attendance

The most notable difference here is that extremely privileged or disadvantaged schools have more extreme values in regions where there are high rates of private school attendance. This is apparent in the relatively steep curves on the extremities of the second plot (“Higher Rates…”). While this data cannot reveal causal relationships, we can say that regions with higher rates of private school attendance have greater inequality among students across students from more and less privileged campuses. We can further examine this by directly comparing racial/ethnic groups.

# Low and high private school attendance

ps <- list()
t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School" & region %in% c(12, 15, 14, 8, 16)), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("High Schools: Small Regions with Lower Rates of Private School Attendance")
ps[[1]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School" & region %in% c(3, 18, 2, 19, 6)), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Student Group`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Student Group`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Student Group`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("High Schools: Small Regions with Higher Rates of Private School Attendance")
ps[[2]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

htmltools::tagList(list(ps[[1]], ps[[2]]))

Racial/Ethnic Group Comparison by Low and High Rates of Regional Private School Attendance

The final exploratory analysis takes the same data and slices it into the three racial/ethnic groups for the lower and higher rates of private school attendance in region. There are at least two descriptive takeaways here:

  1. Schools in regions with lower private school attendance rates tend to have slightly lower rates of meeting their grade levels, for a given campus-level poverty and racial/ethnic group. This is visible in the fact that the red lines (campuses in regions with higher rates of private attendance) are generally slightly higher than the teal line for each racial/ethnic group plot.
  2. Schools in regions with higher rates of private school attendance have higher inequality in performance among campuses within each racial/ethnic group. That is to say that extremely disadvantaged campuses in regions with many private schools perform comparatively worse than other campuses in the same subset of data and the same is true for extremely privileged campuses performing much better in regions with higher rates of private schools.
campus <- campus %>%
  mutate(
    `Private School Rates` = ifelse(
      region %in% c(3, 18, 2, 19, 6), "High and Small Pop.", ifelse(
        region %in% c(12, 15, 14, 8, 16), "Low and Small Pop.", "Middle or Large Pop."))) 
  

t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School" & region %in% c(12, 15, 14, 8, 16, 3, 18, 2, 19, 6) & `Student Group` == "African American"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  #geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Private School Rates`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Private School Rates`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Private School Rates`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("African American Students: High Schools in Small Regions with Lower Rates of Private School Attendance")
ps[[3]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School" & region %in% c(12, 15, 14, 8, 16, 3, 18, 2, 19, 6) & `Student Group` == "Hispanic"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  #geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Private School Rates`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Private School Rates`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Private School Rates`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("Hispanic Students: High Schools in Small Regions with Lower Rates of Private School Attendance")
ps[[4]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

t <-  ggplot(data = campus %>% filter(`Campus Type` == "High School" & region %in% c(12, 15, 14, 8, 16, 3, 18, 2, 19, 6) & `Student Group` == "White"), 
         aes(label = `Campus`, label2 = `Size of Student Group at School`, x = `Eco. Disadvantaged (%)`, y = `Meets Grade Level (%)`)) +
  #geom_line(alpha = .3, aes(group = cid)) +
  geom_point(alpha = .4, aes(size = `Size of Student Group at School`, color = `Private School Rates`)) +
  geom_smooth(size = 2, method = loess, color = "black", se = FALSE, aes(group = `Private School Rates`, weight = ln_meets_denom)) +
  geom_smooth(size = 1.8, method = loess, se = FALSE, aes(color = `Private School Rates`, weight = ln_meets_denom)) +
  guides(size = FALSE) +
  theme(legend.position = "bottom") +
  xlim(c(0,100)) + ylim(c(0,100)) + 
    ggtitle("White Students: High Schools in Small Regions with Lower Rates of Private School Attendance")
ps[[5]] <- ggplotly(t, tooltip = c("label", "label2", "x", "y"))

htmltools::tagList(list(ps[[3]], ps[[4]], ps[[5]]))
LS0tDQp0aXRsZTogIlJhY2UsIEV0aG5pY2l0eSwgUG92ZXJ0eSwgYW5kIEVkdWNhdGlvbmFsIE91dGNvbWVzIGluIFRleGFzIFB1YmxpYyBTY2hvb2xzIg0KDQotLS0NCjxkaXYgYWxpZ249InJpZ2h0Ij4qKk5vdGU6IFRvZ2dsZSB0aGUgImNvZGUiIGJ1dHRvbiB0byB2aWV3L3JlbW92ZSBSIGNvZGUqKjwvZGl2PiAgDQoNClRoaXMgZXhhbXBsZSBoaWdobGlnaHRzIG15IGRhdGEgd3JhbmdsaW5nIGFuZCB2aXN1YWxpemF0aW9uIHNraWxscyBpbiBhbiBleHBsb3JhdG9yeSBhbmFseXNpcyBvZiByYWNpYWwvZXRobmljIGluZXF1aXR5LCBwb3ZlcnR5LCBhbmQgYWNhZGVtaWMgcGVyZm9ybWFuY2UgaW4gcHVibGljIHNjaG9vbHMgaW4gVGV4YXMuIFRoaXMgd2FzIGEgcGV0IHByb2plY3QgdGhhdCBJIGNyZWF0ZWQgdG8gc2F0aXNmeSBteSBvd24gY3VyaW9zaXR5IG9uIHRoZSB0b3BpYyBhbmQgdG8gZ2V0IHRvIGtub3cgdGhlIHB1YmxpY2x5IGF2YWlsYWJsZSBkYXRhIHJlbGF0ZWQgdG8gZWR1Y2F0aW9uIGluIFRleGFzLiBUaGlzIHBhZ2UgcHJvdmlkZXMgdGhlIHN0ZXBzIHJlcXVpcmVkIHRvIGdvIGZyb20gc2V2ZXJhbCByYXcsIHB1YmxpYyBkYXRhIHNldHMgdG8gcHJvZHVjZSBhIHNlcmllcyBvZiBpbnRlcmFjdGl2ZSBkYXRhIHZpc3VhbGl6YXRpb25zIHRoYXQgZXhwbG9yZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gcmFjZS9ldGhuaWNpdHksIHBvdmVydHksIGFuZCBlZHVjYXRpb25hbCBvdXRjb21lcyAoW2xpa2UgdGhlc2VdKGh0dHBzOi8vZGF2aWRybWNjb3kuZ2l0aHViLmlvL0RhdmlkUk1jQ295LmlvL1RYX2VkX2FsbC5odG1sKSksIGFzIHdlbGwgYXMgaG93IHRob3NlIHJlbGF0aW9uc2hpcHMgYXJlIG1vZGVyYXRlZCBieSByZWdpb25hbCBwcml2YXRlIHNjaG9vbCBhdHRlbmRhbmNlLg0KDQojIENhbXB1cy1MZXZlbCBEYXRhIGZvciAyMDE5OiANCg0KIyMgTG9hZCBQYWNrYWdlcw0KWW91IGNhbiB0b2dnbGUgdGhlICJjb2RlIiBidXR0b24gdG8gdGhlIHJpZ2h0IHRvIHNlZSB0aGlzIGNodW5rLiBZb3UgY2FuIGhpZGUgbG9uZyBjb2RlIGNodW5rcyBieSB0b2dnbGluZyB0aGUgc2FtZSBidXR0b24uDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoZ2FwbWluZGVyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShzdW1tYXJ5dG9vbHMpDQpgYGANCg0KIyMgUGVyY2VudCBvZiBTdHVkZW50cyBNZWV0aW5nIEdyYWRlLUxldmVsIGJ5IFJhY2UvRXRobmljIENhdGVnb3JpZXMNCg0KRm9yIGVkdWNhdGlvbmFsIG91dGNvbWVzLCBJIHVzZSB0aGUgcHVibGljbHkgYXZhaWxhYmxlIFtUZXhhcyBBY2FkZW1pYyBQZXJmb3JtYW5jZSBSZXBvcnQgKFRBUFIpIGRhdGFdKGh0dHBzOi8vcnB0c3ZyMS50ZWEudGV4YXMuZ292L3BlcmZyZXBvcnQvdGFwci8yMDE5L2Rvd25sb2FkL0Rvd25sb2FkRGF0YS5odG1sKSBmb3IgdGhlIHllYXIgMjAxOS4gVGhpcyBpcyBhIHZlcnkgbGFyZ2UgZGF0YSBzZXQgdGhhdCB0aGF0IEkgaGF2ZSBzdWJzZXR0ZWQgdG8gZm9jdXMgb24gdGhlIHBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMgaW4gdGhyZWUgcmFjaWFsL2V0aG5pYyBjYXRlZ29yaWVzICh2YXJpYWJsZTogYFN0dWRlbnQgR3JvdXBgKSB3aG8gbWVldCB0aGUgZXhwZWN0YXRpb25zIG9mIHRoZWlyIHJlc3BlY3RpdmUgZ3JhZGUgbGV2ZWxzICh2YXJpYWJsZTogYE1lZXRzIEdyYWRlIExldmVsICglKWApLiBJIGFsc28gaW5jbHVkZSB0aGUgbnVtZXJpYyBjYW1wdXMgaWRlbnRpZmllciBgY2lkYCBhbmQgdGhlIHJlc3BlY3RpdmUgZGVub21pbmF0b3JzIGZvciBlYWNoIHBlcmNlbnRpbGUgdmFsdWUgKHZhcmlhYmxlOiBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGApLCB3aGljaCBpbmZvcm1zIHRoZSBhYnNvbHV0ZSBudW1iZXIgb2Ygc3R1ZGVudHMgZnJvbSBlYWNoIGNhdGVnb3J5IGF0IHRoZSBzY2hvb2wuIEkgdXNlIHRoaXMgdmFsdWUgdG8gd2VpZ2h0IHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIExvZXNzIHNtb290aGluZyBmdW5jdGlvbiBhbmQgdG8gaW5mb3JtIHRoZSBzaXplIHBhcmFtZXRlciBpbiB0aGUgcGxvdHMuIEZvciB0aGUgb3JpZ2luYWwgdmFyaWFibGUgbmFtZXMsIHNlZSB0aGUgW1RBUFIgQ29kZWJvb2tdKGh0dHBzOi8vcnB0c3ZyMS50ZWEudGV4YXMuZ292L3BlcmZyZXBvcnQvdGFwci8yMDE5L2Rvd25sb2FkL2NhbXBzdGFhcjJiLmh0bWwpLiAgDQoNCkFzIGRlc2NyaWJlZCBpbiB0aGUgW2NvZGVib29rIGZvciB0aGUgZGF0YSBzZXRdKGh0dHBzOi8vcnB0c3ZyMS50ZWEudGV4YXMuZ292L3BlcmZyZXBvcnQvdGFwci8yMDE5L2Rvd25sb2FkL2NhbXBzdGFhcjJiLmh0bWwpLCBzdHVkZW50IGdyb3VwcycgcGVyY2VudGlsZSB2YWx1ZXMgd2l0aCB2ZXJ5IHNtYWxsIGRlbm9taW5hdG9ycyBhcmUgbWFza2VkIChjb2RlZCBhcyBuZWdhdGl2ZSBwbGFjZWhvbGRlciB2YWx1ZXMgb2YgIi0xIikgaW4gb3JkZXIgdG8gcHJlY2x1ZGUgdGhlIHBvc3NpYmlsaXR5IG9mIGlkZW50aWZpY2F0aW9uIG9mIGluZGl2aWR1YWxzIGluIHRoZSBkYXRhLiBJIGV4Y2x1ZGUgdGhlc2UgbWFza2VkIHZhbHVlcyBmcm9tIHRoZSBkYXRhIHNldCwgYXMgd2VsbCBhcyBhbnkgc2Nob29sIHRoYXQgcmVwb3J0cyBtaXNzaW5nIGRhdGEgZm9yIGFsbCB0aHJlZSBzdHVkZW50IGdyb3Vwcy4gRGVub21pbmF0b3JzIG9mIHRoZSBzZWNvbmQgc21hbGxlc3Qgc3R1ZGVudCBncm91cCBmb3IgZWFjaCBzY2hvb2wgYXJlIGFsc28gbWFza2VkIHdpdGggYSB1bmlxdWUgcGxhY2Vob2xkZXIgKCItMyIpIHdoZW4gdGhlIHNtYWxsZXN0IGdyb3VwIHJlcXVpcmVzIG1hc2tpbmcuIEkgcmVwbGFjZSB0aGlzIHNldCBvZiBtYXNrZWQgdmFsdWVzIHdpdGggYSByZWFzb25hYmxlIHByb3h5IG9mIGhhbGYgdGhlIHNpemUgb2YgdGhlIG5leHQgbGFyZ2VzdCBncm91cCBpbiB0aGUgZGF0YS4gIA0KDQpUaGlzIGNvZGUgZG9lcyB0aGUgZm9sbG93aW5nOiAgDQoNCjEuIFJlYWQgaW4gc3Vic2V0IG9mIFRBUFIgRGF0YQ0KMi4gRmlsdGVyIHNjaG9vbHMgd2l0aCBhbGwgbWlzc2luZyB2YWx1ZXMNCjMuIFBpdm90IHRoZSBkYXRhIHRvIGluY2x1ZGUgKHVwIHRvKSB0aHJlZSBvYnNlcnZhdGlvbnMgcGVyIHNjaG9vbCwgY29ycmVzcG9uZGluZyB0byB0aGUgdGhyZWUgcGVyY2VudGlsZSBzY29yZXMgYE1lZXRzIEdyYWRlIExldmVsICglKWAgb2Ygc3R1ZGVudHMgaW4gZWFjaCByYWNpYWwvZXRobmljIGNhdGVnb3J5IHRoYXQgbWVldCB0aGUgZ3JhZGUtbGV2ZWwgZXhwZWN0YXRpb25zLg0KNC4gQ3JlYXRlIGNhdGVnb3J5IG5hbWVzIGZvciBzdHVkZW50IGdyb3VwczogYFN0dWRlbnQgR3JvdXBgIGFuZCBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGANCjUuIFJlbW92ZSBtYXNrZWQgZGF0YSBmb3IgcGVyY2VudGlsZXMgYW5kIHJlY29kZSB0aGUgbWFza2VkIGRlbm9taW5hdG9yIGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCB3aXRoIHRoZSBwcm94eS4NCjYuIENyZWF0ZSBhbiBhZGRpdGlvbmFsIHZhcmlhYmxlIGBsbl9tZWV0c19kZW5vbWAgb2YgbG9nZ2VkIGdyb3VwIHNpemUgZm9yIHVzZSBhcyB3ZWlnaHQgaW4gdGhlIExvZXNzIHNtb290aGluZyBhbGdvcml0aG0uICANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIExvYWQgVEFQUiBkYXRhOyByZW5hbWUgdmFycw0KY2FtcHVzXzIwMTkgPC0gcmVhZC5jc3YoImRvY3MvVEFQUl8yMDE5X3N1YnNldC5jc3YiKSAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIGNpZCA9IENBTVBVUywNCiAgICBgQWZyaWNhbiBBbWVyaWNhbmAgPSBDREIwMEEwMDEyMTlSLA0KICAgIGBIaXNwYW5pY2AgPSBDREgwMEEwMDEyMTlSLA0KICAgIGBXaGl0ZWAgPSBDRFcwMEEwMDEyMTlSLA0KICAgIGFhX3RvdGFsID0gQ0RCMDBBMDAxMDE5RCwNCiAgICBoX3RvdGFsID0gQ0RIMDBBMDAxMDE5RCwNCiAgICB3X3RvdGFsID0gQ0RXMDBBMDAxMDE5RCkgJT4lDQogICMgRmlsdGVyIG91dCBzY2hvb2xzIHdpdGggbm8gZGF0YSAofiAxMCUgb2YgY2FtcHVzZXMpDQogIGZpbHRlcighaXMubmEoYEFmcmljYW4gQW1lcmljYW5gKSB8ICFpcy5uYShgSGlzcGFuaWNgKSB8ICFpcy5uYShgV2hpdGVgKSkNCiMgUGl2b3QgdmFsdWVzIGFuZCBkZXNjcmlwdGl2ZSBjYXRlZ29yaWVzDQpkYXRhIDwtIGNiaW5kKA0KICBwaXZvdF9sb25nZXIoY2FtcHVzXzIwMTkgJT4lIHNlbGVjdCgxOjQpLCBjb2xzID0gMjo0LCBuYW1lc190byA9ICJTdHVkZW50IEdyb3VwIiwgdmFsdWVzX3RvID0gIk1lZXRzIEdyYWRlIExldmVsICglKSIpLA0KICBwaXZvdF9sb25nZXIoY2FtcHVzXzIwMTkgJT4lIHNlbGVjdCg1OjcpLCBjb2xzID0gMTozLCBuYW1lc190byA9ICJncm91cF9kZW5vbSIsIHZhbHVlc190byA9ICJTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sIikpICU+JQ0KICBncm91cF9ieShjaWQpICU+JQ0KICBtdXRhdGUoDQogICAgYE1lZXRzIEdyYWRlIExldmVsICglKWAgPSBpZmVsc2UoYE1lZXRzIEdyYWRlIExldmVsICglKWAgPT0gLTEsIE5BLCBgTWVldHMgR3JhZGUgTGV2ZWwgKCUpYCksICMgcmF0ZSA9IC0xIGlzIG1hc2tlZCBkYXRhIGFuZCB1bnVzYWJsZQ0KICAgIGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCA9IGlmZWxzZShgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAgPT0gLTEsIE5BLCAjIGRlbm9taW5hdG9yID0gLTEgaXMgbWFza2VkIGRhdGEgYW5kIHVudXNhYmxlDQogICAgICAgICAgICAgICAgICAgICBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGApLCANCiAgICBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAgPSBpZmVsc2UoYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgID09IC0zLCAjIGRlbm9taW5hdG9yID0gLTMgaXMgc2Vjb25kIHNtYWxsZXN0IGdyb3VwDQogICAgICAgICAgICAgICAgICAgICBudGgoYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCAyLCBvcmRlcl9ieSA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCkgLyAyLCANCiAgICAgICAgICAgICAgICAgICAgIGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCksICMgcmVjb3ZlciB3aXRoIHJlYXNvbmFibGUgcHJveHkgb2YgaGFsZiBsYXJnZXN0IGdyb3VwDQogICAgbG5fbWVldHNfZGVub20gPSBsb2coYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgKSkgJT4lICMgbG9nIHdpbGwgYmUgYmV0dGVyIGZvciBzaXplIHBhcmFtZXRlcg0KICB1bmdyb3VwKCkNCiMgU2hvdyBEYXRhDQpkYXRhWzE6OSxdICU+JQ0KICBrYmwoY2FwdGlvbiA9ICJTdHJ1Y3R1cmUgb2YgdGhlIERhdGEgU2V0OiBTaG93aW5nIHRoZSBGaXJzdCBUaHJlZSBTY2hvb2xzIikgJT4lDQogIGthYmxlX3BhcGVyKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCiMjIENhbXB1cy1MZXZlbCBQb3ZlcnR5IERhdGENCg0KRm9yIGNhbXB1cy1sZXZlbCBwb3ZlcnR5IHN0YXRpc3RpY3MsIEkgdXNlIHRoZSBwdWJsaWMgZGF0YSBhdmFpbGFibGUgYXQgdGhlIFRleGFzIEVkdWNhdGlvbiBBZ2VuY3kncyAoVEVBKSBbc2Nob29sIGRhdGEgdmlzdWFsaXplcl0oaHR0cHM6Ly9ycHRzdnIxLnRlYS50ZXhhcy5nb3YvcGVyZnJlcG9ydC9hY2NvdW50L3ZhL3ZhX2NvcnJlbGF0ZS5odG1sKSwgd2hpY2ggaW5jbHVkZXMgYW4gZXh0ZW5zaXZlIHNldCBvZiBjYW1wdXMtbGV2ZWwgYXR0cmlidXRlcy4gSSBhbSBwcmltYXJpbHkgaW50ZXJlc3RlZCBpbiB0aGUgcGVyY2VudGFnZSBvZiBzdHVkZW50cyBmcm9tIGVjb25vbWljYWxseSBkaXNhZHZhbnRhZ2VkIGJhY2tncm91bmRzLCBtZWFzdXJlZCBhcyB0aGUgcGVyY2VudGFnZSBvZiBzdHVkZW50cyAiLi4uU3R1ZGVudHMgZWxpZ2libGUgZm9yIGZyZWUgb3IgcmVkdWNlZC1wcmljZSBsdW5jaCBvciBvdGhlciBwdWJsaWMgYXNzaXN0YW5jZSBhcyByZXBvcnRlZCBvbiB0aGUgUEVJTVMgT2N0b2JlciBzbmFwc2hvdCIgKFtzZWUgZGVmaW5pdGlvbnNdKGh0dHBzOi8vcnB0c3ZyMS50ZWEudGV4YXMuZ292L3BlcmZyZXBvcnQvZmFxc2l0ZS9nbG9zc2FyeS5odG1sKSkuIFVuZm9ydHVuYXRlbHksIHRoZSBzb3VyY2UgZG9lcyBub3QgcmV2ZWFsIHByZWNpc2VseSB3aGljaCB5ZWFyIHRoaXMgZGF0YSBjb21lcyBmcm9tISBTY2hvb2wtbGV2ZWwgcG92ZXJ0eSBpcyBhIHJlbGF0aXZlbHkgc3RhYmxlIGF0dHJpYnV0ZSwgdGhvdWdoLCBzbyB0aGlzIGlzIG5vdCBhIGh1Z2UgY29uY2Vybi4gIA0KDQpUaGUgY2hhbGxlbmdlIGlzIHRoYXQgdGhpcyBkYXRhIGRvZXMgbm90IGluY2x1ZGUgdGhlIGNhbXB1cyBJRCBhcyBhIHN0YW5kLWFsb25lIHZhcmlhYmxlLiBJIG11c3QgZmlyc3QgZXh0cmFjdCBpdCBmcm9tIGEgbG9uZ2VyIHN0cmluZyB0byBjcmVhdGUgYSBjb21wYXJhYmxlIGBjaWRgIHZhcmlhYmxlIHRoYXQgY2FuIGJlIHVzZWQgdG8gam9pbiB0aGlzIGRhdGEgc2V0IHRvIHRoZSBmaXJzdCBvbmUuIEJlY2F1c2UgdGhlcmUgaXMgbm8gdW5pdmVyc2FsIHBhdHRlcm4gdG8gcGFyc2UgdGhpcyBzdHJpbmcsIEkgaGF2ZSB0byBzcGxpdCBvbiB0aHJlZSBkaWZmZXJlbnQgcGF0dGVybnMgYW5kIGV4dHJhY3QgdGhlIHJpZ2h0bW9zdCBlbGVtZW50IGluIG9yZGVyIHRvIHJlY292ZXIgYWxsIHRoZSBjYW1wdXMgaWRlbnRpZmllcnMuIFRoaXMgaXMgYmVjYXVzZSBzb21lIHNjaG9vbHMgaGF2ZSBtb3JlIHRoYW4gb25lIHNldCBvZiB0aGUgIiAoIiBwYXR0ZXJuIG9uIHRoZSByaWdodC1oYW5kIHNpZGUgb2YgdGhlICJ8fCIgcGF0dGVybi4gVGhpcyBpcyBjb21wbGljYXRlZDsgaG93ZXZlciwgdGhlIGVuZCByZXN1bHQgaXMgdGhlIGV4dHJhY3Rpb24gb2YgdGhlIGBjaWRgIHZhcmlhYmxlIGZvciBlYWNoIHNjaG9vbCwgd2hpY2ggSSB2YWxpZGF0ZWQgdG8gZW5zdXJlIHRoYXQgYWxsIHNjaG9vbHMgYXJlIGFjY291bnRlZCBmb3IuDQoNCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0Kc2Nob29sIDwtIHJlYWRfZXhjZWwoImRvY3Mvc2Nob29sLnhsc3giKQ0Kc2Nob29sIDwtIHNjaG9vbCAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIENhbXB1cyA9IGBDYW1wdXMgb3IgRGlzdHJpY3RgLCANCiAgICBgRWNvLiBEaXNhZHZhbnRhZ2VkICglKWAgPSBgJSBFY28gRGlzYWR2YW50YWdlZGAsDQogICAgYENhbXB1cyBUeXBlYCA9IGZhY3RvcihgRW50aXR5IFR5cGVgKSwNCiAgICBlbnJvbGxtZW50X3B1YmxpYyA9IEVucm9sbG1lbnQpICU+JQ0KICBtdXRhdGUoDQogICAgbiA9IHN0cl9zcGxpdChDYW1wdXMsICIgXFx8XFx8Iiwgc2ltcGxpZnkgPSBUUlVFKVssMl0sICMgRmlyc3Qgc3BsaXQgb24gInx8Ig0KICAgIG5uID0gc3RyX3NwbGl0KG4sICIgXFwoIiwgc2ltcGxpZnkgPSBUUlVFKVssMl0sICMgTmV4dCBzcGxpdCBvbiAiICgiDQogICAgbm5uID0gc3RyX3NwbGl0KG4sICIgXFwoIiwgc2ltcGxpZnkgPSBUUlVFKVssM10sICMgQSBmZXcgaGF2ZSBhbiBhZGRpdGlvbmFsICIgKCINCiAgICBjaWQgPSBhcy5udW1lcmljKGdzdWIoIlxcRCsiLCAiIiwgbm4pKSwNCiAgICBjaWQgPSBpZmVsc2UoaXMubmEoY2lkKSwgYXMubnVtZXJpYyhnc3ViKCJcXEQrIiwgIiIsIG5ubikpLCBjaWQpKSAlPiUNCiAgc2VsZWN0KC1zdGFydHNfd2l0aCgibiIpKQ0KDQpzY2hvb2xbMTo1LF0gJT4lDQogIGtibChjYXB0aW9uID0gIlN0cnVjdHVyZSBvZiB0aGUgUG92ZXJ0eSBEYXRhIGFmdGVyIFJlY292ZXJpbmcgdGhlIENhbXB1cyBJZGVudGlmaWVyIikgJT4lDQogIGthYmxlX3BhcGVyKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQ0KDQpgYGANCg0KIyMgQ29kZXM6IFNjaG9vbCwgRGlzdHJpY3QsIFJlZ2lvbiwgQ291bnR5DQoNClRoaXMgZGF0YSBzZXQgZnJvbSBURUEgaW5jbHVkZXMgdGhlIGNvZGVzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGdlb2dyYXBoaWMgaWRlbnRpZmljYXRpb24gaW4gdGhlIHBsb3RzLiBJIHVzZSB0aGUgYHJlZ2lvbmAgY29kZSB0byBkaXNhZ2dyZWdhdGUgdGhlIGZ1bGwgZGF0YSBzZXQgaW50byBtYW5hZ2VhYmxlIHN1YnNldHMgYW5kIHRvIGpvaW4gdGhpcyBkYXRhIHdpdGggdGhlIHByaXZhdGUgc2Nob29scyBkYXRhIGluIHRoZSBmaW5hbCBzZXQgb2YgYW5hbHlzZXMuDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0NCmNvZGVzIDwtIHJlYWRfY3N2KCJkb2NzL3NjaG9vbCBhbmQgZGlzdHJpY3QuY3N2IikNCmNvZGVzIDwtIGNvZGVzICU+JQ0KICB0cmFuc211dGUoDQogICAgY2lkID0gYXMubnVtZXJpYyhnc3ViKCJbXls6YWxudW06XSBdIiwgIiIsIGBTY2hvb2wgTnVtYmVyYCkpLA0KICAgIGRpc3RyaWN0ID0gYXMubnVtZXJpYyhnc3ViKCJbXls6YWxudW06XSBdIiwgIiIsIGBEaXN0cmljdCBOdW1iZXJgKSksDQogICAgY291bnR5ID0gYXMubnVtZXJpYyhnc3ViKCJbXls6YWxudW06XSBdIiwgIiIsIGBDb3VudHkgTnVtYmVyYCkpLA0KICAgIHJlZ2lvbiA9IGFzLm51bWVyaWMoZ3N1YigiW15bOmFsbnVtOl0gXSIsICIiLCBgRVNDIFJlZ2lvbiBTZXJ2ZWRgKSkpDQoNCmBgYA0KDQoNCg0KIyMgSm9pbiBEYXRhIGFuZCBTZWUgRGVzY3JpcHRpdmUgU3RhdGlzdGljcw0KDQpUYWtpbmcgdGhlIFRBUFIgMjAxOSBkYXRhIGFzIHRoZSBiYXNlIHNldCwgSSBtYXRjaCBhcHByb3hpbWF0ZWx5IDk4LjMlIG9mIGNhbXB1c2VzIHdpdGggdGhlIFRFQSBzY2hvb2wtbGV2ZWwgZGF0YSBieSB0aGVpciBjYW1wdXMgaWQgKGBjaWRgKS4gRm9yIHBhcnNpbW9ueSwgSSBleGNsdWRlIHRob3NlICUxLjclIGNhbXB1c2VzIHRoYXQgZG8gbm90IGV4aXN0IGluIGJvdGggZGF0YSBzZXRzLiBXZSBjYW4gc2VlIHRoYXQgdGhlIG1hc2tpbmcgYW5kIG1pc3NpbmcgZGF0YSByZXN1bHRzIGluIGFwcHJveGltYXRlbHkgMTAlIG9mIG1pc3NpbmcgZGF0YSBmcm9tIHRoZSBUQVBSIDIwMTkgZGF0YTsgaG93ZXZlciwgZHVlIHRvIHRoZSBsb2dpYyBvZiBob3cgdGhlIG1hc2tpbmcgaXMgY29kZWQsIHRob3NlIG1hc2tlZCBvYnNlcnZhdGlvbnMgYXJlIGxpa2VseSB0byBiZSBzbWFsbGVyLCByZWxhdGl2ZWx5IG1vcmUgaG9tb2dlbmVvdXMgc2Nob29scywgb3RoZXJ3aXNlLCB0aGV5IHdvdWxkIG5vdCBoYXZlIHZlcnkgc21hbGwgYWJzb2x1dGUgbnVtYmVycyBvZiBzdHVkZW50cyBmcm9tIGFueSBvZiB0aGUgdGhyZWUgcmFjaWFsL2V0aG5pYyBiYWNrZ3JvdW5kcy4gIA0KDQpgYGB7ciwgcmVzdWx0cyA9ICJhc2lzIiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQpjYW1wdXMgPC0gbGVmdF9qb2luKHNjaG9vbCwgY29kZXMsIGJ5ID0gImNpZCIpICU+JSByaWdodF9qb2luKGRhdGEsIGJ5ID0gImNpZCIpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKENhbXB1cykpDQpwcmludCgNCiAgZGZTdW1tYXJ5KGNhbXB1cywNCiAgICAgICAgICBwbGFpbi5hc2NpaSAgPSBGQUxTRSwgDQogICAgICAgICAgc3R5bGUgICAgICAgID0gImdyaWQiLCANCiAgICAgICAgICBncmFwaC5tYWduaWYgPSAwLjc1LCANCiAgICAgICAgICB2YWxpZC5jb2wgICAgPSBGQUxTRSwNCiAgICAgICAgICB0bXAuaW1nLmRpciAgPSAiL3RtcCIsDQogICAgICAgICAgc2lsZW50ID0gVFJVRSksDQogIG1ldGhvZCA9ICJyZW5kZXIiKQ0KYGBgDQoNCg0KIyBFeHBsb3JpbmcgdGhlIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIFJhY2UvRXRobmljaXR5LCBQb3ZlcnR5LCBhbmQgRWR1Y2F0aW9uIE91dGNvbWVzDQoNCkhlcmUgSSB3cml0ZSBhIGZ1bmN0aW9uIHRvIGJ1aWxkIGBwbG90bHlgIGludGVyYWN0aXZlIHZpc3VhbGl6YXRpb25zLiBSZWFkZXJzIGNhbiBob3ZlciBvdmVyIHRoZSBpbWFnZSB0byBnZXQgcG9pbnQtbGV2ZWwgZGF0YSwgYXMgd2VsbCBhcyB0byB6b29tIGluIHRvIGEgcGxvdCByZWdpb24gYW5kIHRvZ2dsZSBhIGBTdHVkZW50IEdyb3VwYCBvbi9vZmYgaW4gdGhlIHBvaW50L2NvbG9yIGxlZ2VuZC4gU2Nob29sLWxldmVsIGRhdGEgaXMgcGxvdHRlZCBvbiB0aGUgeC1heGlzIGJhc2VkIG9uIHBvdmVydHkgbGV2ZWxzIGBFY28uIERpc2FkdmFudGVkICglKWAuIFNjaG9vbC1sZXZlbCBkYXRhIGRpc2FnZ3JlZ2F0ZWQgYnkgcmFjZS9ldGhuaWNpdHkgb24gdGhlIHktYXhpcyB0byBzaG93IGVhY2ggcmFjaWFsL2V0aG5pYyBncm91cCdzIGFjYWRlbWljIHBlcmZvcm1hbmNlIGFzIGBNZWV0cyBHcmFkZSBMZXZlbCAoJSlgLiBUaGUgdGhyZWUgcmFjZS9ldGhuaWMgZ3JvdXBzIGFyZSBjb25uZWN0ZWQgYnkgdmVydGljYWwgbGluZXMsIHNob3dpbmcgdGhlIGRpc3RhbmNlIG9yIHNwcmVhZCAoeS1heGlzKSBpbiBwZXJmb3JtYW5jZSBhbW9uZyB0aGUgZ3JvdXBzIHdpdGhpbiBlYWNoIGNhbXB1cy4gVGhlIHNpemUgb2YgZWFjaCBwb2ludCBvbiB0aGUgcGxvdCBpcyBtYXBwZWQgYnkgdGhlIGFic29sdXRlIG51bWJlciBvZiBzdHVkZW50cyBpbiBlYWNoIHJhY2UvZXRobmljaXR5IGNhdGVnb3J5IHdpdGhpbiB0aGF0IHNjaG9vbC4gVGhlIGxpbmVzIGFyZSBnZW5lcmF0ZWQgdGhyb3VnaCBMb2VzcyBzbW9vdGhpbmcgYWxnb3JpdGhtcyB0aGF0IGRpc3BsYXkgKG5vbi1saW5lYXIpIHRyZW5kcyBhY3Jvc3MgdGhlIHgtYXhpcy4NCg0KIyMgQ3JlYXRlIEZ1bmN0aW9ucyBmb3IgSW50ZXJhY3RpdmUgUGxvdHMNCg0KSSB3cml0ZSBhIGN1c3RvbSBmdW5jdGlvbiB0aGF0IHRha2VzIHRoZSBkYXRhIGFuZCB0d28gZmlsdGVyaW5nIHBhcmFtZXRlcnMgdG8gc2xpY2UgdGhlIGZ1bGwgZGF0YSBzZXQ6IGBjYW1wdXMgJT4lIGZpbHRlcignQ2FtcHVzIFR5cGUnID09IHt7dHlwZX19ICYgcmVnaW9uID09IHt7cn19KWAuIEkgcGFzcyB0aGlzIGZ1bmN0aW9uIHRvIGBsYXBwbHkoKWAgdG8gZ2VuZXJhdGUgcGxvdHMgZm9yIGVhY2ggYHJlZ2lvbmAgYW5kIGBDYW1wdXMgVHlwZWAgKGV4Y2x1ZGluZyBLLTEyIHNjaG9vbHMgZm9yIHBhcnNpbW9ueSkuDQoNCmBgYHtyLCByZXN1bHRzID0gImFzaXMiLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0NCiMgUmVnaW9ucyAxIHRvIDIwDQpyIDwtIDE6MjANCiMgQmxhbmsgbGlzdCBmb3IgZnVsbCBzYW1wbGUgb2JqZWN0cw0KZnMgPC0gbGlzdCgpDQojIENyZWF0ZSBmdW5jdGlvbiB0byBnZW5lcmF0ZSAyMCBwbG90cw0KcGxvdHMgPC0gZnVuY3Rpb24oZGF0YSwgciwgdHlwZSkgew0KDQp0IDwtICBnZ3Bsb3QoZGF0YSA9IGNhbXB1cyAlPiUgZmlsdGVyKGBDYW1wdXMgVHlwZWAgPT0ge3t0eXBlfX0gJiByZWdpb24gPT0ge3tyfX0pLCANCiAgICAgICAgIGFlcyhsYWJlbCA9IGBDYW1wdXNgLCBsYWJlbDIgPSBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAsIHggPSBgRWNvLiBEaXNhZHZhbnRhZ2VkICglKWAsIHkgPSBgTWVldHMgR3JhZGUgTGV2ZWwgKCUpYCkpICsNCiAgZ2VvbV9saW5lKGFscGhhID0gLjMsIGFlcyhncm91cCA9IGNpZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40LCBhZXMoc2l6ZSA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgY29sb3IgPSBgU3R1ZGVudCBHcm91cGApKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyLCBtZXRob2QgPSBsb2VzcywgY29sb3IgPSAiYmxhY2siLCBzZSA9IEZBTFNFLCBhZXMoZ3JvdXAgPSBgU3R1ZGVudCBHcm91cGAsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMS44LCBtZXRob2QgPSBsb2Vzcywgc2UgPSBGQUxTRSwgYWVzKGNvbG9yID0gYFN0dWRlbnQgR3JvdXBgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICB4bGltKGMoMCwxMDApKSArIHlsaW0oYygwLDEwMCkpICsgDQogICAgZ2d0aXRsZShwYXN0ZSgiUmVnaW9uIiwge3tyfX0sIHt7dHlwZX19LCAiUGVyZm9ybWFuY2UgYnkgUmFjZS9FdGhuaWNpdHkgYW5kIFBvdmVydHkiLCBzZXAgPSAiICIpKQ0KICANCnRbW3JdXSA8LSBnZ3Bsb3RseSh0LCB0b29sdGlwID0gYygibGFiZWwiLCAibGFiZWwyIiwgIngiLCAieSIpKQ0KDQojcHJpbnQodFtbcl1dKQ0KI3JldHVybih0W1tyXV0pDQp9DQoNCiMgU3RvcmVzIGhpZ2ggc2Nob29sIHBsb3RzIGZvciAyMCByZWdpb25zIGluIGluZGV4ZWQgbGlzdA0KZ2dfaHMgPC0gbGFwcGx5KHIsIHBsb3RzLCBkYXRhID0gY2FtcHVzLCB0eXBlID0gIkhpZ2ggU2Nob29sIikNCg0KIyBTdG9yZXMgaGlnaCBzY2hvb2wgcGxvdHMgZm9yIDIwIHJlZ2lvbnMgaW4gaW5kZXhlZCBsaXN0DQpnZ19qaHMgPC0gbGFwcGx5KHIsIHBsb3RzLCBkYXRhID0gY2FtcHVzLCB0eXBlID0gIk1pZGRsZSAmIEpyLiBIaWdoIFNjaG9vbCIpDQoNCiMgU3RvcmVzIGVsZW1lbnRhcnkgc2Nob29sIHBsb3RzIGZvciAyMCByZWdpb25zIGluIGluZGV4ZWQgbGlzdA0KZ2dfZXMgPC0gbGFwcGx5KHIsIHBsb3RzLCBkYXRhID0gY2FtcHVzLCB0eXBlID0gIkVsZW1lbnRhcnkgU2Nob29sIikNCg0KIyBGdWxsIHNhbXBsZTogSGlnaCBzY2hvb2wNCnQgPC0gIGdncGxvdChkYXRhID0gY2FtcHVzICU+JSBmaWx0ZXIoYENhbXB1cyBUeXBlYCA9PSAiSGlnaCBTY2hvb2wiKSwgDQogICAgICAgICBhZXMobGFiZWwgPSBgQ2FtcHVzYCwgbGFiZWwyID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCB4ID0gYEVjby4gRGlzYWR2YW50YWdlZCAoJSlgLCB5ID0gYE1lZXRzIEdyYWRlIExldmVsICglKWApKSArDQogIGdlb21fbGluZShhbHBoYSA9IC4zLCBhZXMoZ3JvdXAgPSBjaWQpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAuNCwgYWVzKHNpemUgPSBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAsIGNvbG9yID0gYFN0dWRlbnQgR3JvdXBgKSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMiwgbWV0aG9kID0gbG9lc3MsIGNvbG9yID0gImJsYWNrIiwgc2UgPSBGQUxTRSwgYWVzKGdyb3VwID0gYFN0dWRlbnQgR3JvdXBgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDEuOCwgbWV0aG9kID0gbG9lc3MsIHNlID0gRkFMU0UsIGFlcyhjb2xvciA9IGBTdHVkZW50IEdyb3VwYCwgd2VpZ2h0ID0gbG5fbWVldHNfZGVub20pKSArDQogIGd1aWRlcyhzaXplID0gRkFMU0UpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgeGxpbShjKDAsMTAwKSkgKyB5bGltKGMoMCwxMDApKSArIA0KICAgIGdndGl0bGUoIkZ1bGwgU2FtcGxlIG9mIEhpZ2ggU2Nob29sczogUGVyZm9ybWFuY2UgYnkgUmFjZS9FdGhuaWNpdHkgYW5kIFBvdmVydHkiKQ0KZnNbWzFdXSA8LSBnZ3Bsb3RseSh0LCB0b29sdGlwID0gYygibGFiZWwiLCAibGFiZWwyIiwgIngiLCAieSIpKQ0KDQojIEZ1bGwgc2FtcGxlOiBKci4gSGlnaCBzY2hvb2wNCnQgPC0gIGdncGxvdChkYXRhID0gY2FtcHVzICU+JSBmaWx0ZXIoYENhbXB1cyBUeXBlYCA9PSAiTWlkZGxlICYgSnIuIEhpZ2ggU2Nob29sIiksIA0KICAgICAgICAgYWVzKGxhYmVsID0gYENhbXB1c2AsIGxhYmVsMiA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgeCA9IGBFY28uIERpc2FkdmFudGFnZWQgKCUpYCwgeSA9IGBNZWV0cyBHcmFkZSBMZXZlbCAoJSlgKSkgKw0KICBnZW9tX2xpbmUoYWxwaGEgPSAuMywgYWVzKGdyb3VwID0gY2lkKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjQsIGFlcyhzaXplID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCBjb2xvciA9IGBTdHVkZW50IEdyb3VwYCkpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDIsIG1ldGhvZCA9IGxvZXNzLCBjb2xvciA9ICJibGFjayIsIHNlID0gRkFMU0UsIGFlcyhncm91cCA9IGBTdHVkZW50IEdyb3VwYCwgd2VpZ2h0ID0gbG5fbWVldHNfZGVub20pKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAxLjgsIG1ldGhvZCA9IGxvZXNzLCBzZSA9IEZBTFNFLCBhZXMoY29sb3IgPSBgU3R1ZGVudCBHcm91cGAsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBndWlkZXMoc2l6ZSA9IEZBTFNFKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIHhsaW0oYygwLDEwMCkpICsgeWxpbShjKDAsMTAwKSkgKyANCiAgICBnZ3RpdGxlKCJGdWxsIFNhbXBsZSBvZiBNaWRkbGUgU2Nob29sczogUGVyZm9ybWFuY2UgYnkgUmFjZS9FdGhuaWNpdHkgYW5kIFBvdmVydHkiKQ0KZnNbWzJdXSA8LSBnZ3Bsb3RseSh0LCB0b29sdGlwID0gYygibGFiZWwiLCAibGFiZWwyIiwgIngiLCAieSIpKQ0KDQojIEZ1bGwgc2FtcGxlOiBFbGVtZW50YXJ5IHNjaG9vbA0KdCA8LSAgZ2dwbG90KGRhdGEgPSBjYW1wdXMgJT4lIGZpbHRlcihgQ2FtcHVzIFR5cGVgID09ICJFbGVtZW50YXJ5IFNjaG9vbCIpLCANCiAgICAgICAgIGFlcyhsYWJlbCA9IGBDYW1wdXNgLCBsYWJlbDIgPSBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAsIHggPSBgRWNvLiBEaXNhZHZhbnRhZ2VkICglKWAsIHkgPSBgTWVldHMgR3JhZGUgTGV2ZWwgKCUpYCkpICsNCiAgZ2VvbV9saW5lKGFscGhhID0gLjMsIGFlcyhncm91cCA9IGNpZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40LCBhZXMoc2l6ZSA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgY29sb3IgPSBgU3R1ZGVudCBHcm91cGApKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyLCBtZXRob2QgPSBsb2VzcywgY29sb3IgPSAiYmxhY2siLCBzZSA9IEZBTFNFLCBhZXMoZ3JvdXAgPSBgU3R1ZGVudCBHcm91cGAsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMS44LCBtZXRob2QgPSBsb2Vzcywgc2UgPSBGQUxTRSwgYWVzKGNvbG9yID0gYFN0dWRlbnQgR3JvdXBgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICB4bGltKGMoMCwxMDApKSArIHlsaW0oYygwLDEwMCkpICsgDQogICAgZ2d0aXRsZSgiRnVsbCBTYW1wbGUgb2YgRWxlbWVudGFyeSBTY2hvb2xzOiBQZXJmb3JtYW5jZSBieSBSYWNlL0V0aG5pY2l0eSBhbmQgUG92ZXJ0eSIpDQpmc1tbM11dIDwtIGdncGxvdGx5KHQsIHRvb2x0aXAgPSBjKCJsYWJlbCIsICJsYWJlbDIiLCAieCIsICJ5IikpDQoNCg0KYGBgDQoNCiMjIFBsb3RzOiBSYWNlL0V0aG5pY2l0eSwgUG92ZXJ0eSwgYW5kIEVkdWNhdGlvbiBPdXRjb21lcw0KDQpUaGUgZnVuY3Rpb25zIGhhdmUgc3RvcmVkIHRocmVlIHNldHMgb2YgMjAgcGxvdHMgd2l0aGluIGluZGV4ZWQgbGlzdHMgLSBvbmUgcGxvdCBmb3IgZWFjaCBUZXhhcyBFZHVjYXRpb24gU2VydmljZSBDZW50ZXIgUmVnaW9uIGF0IGVhY2ggY2FtcHVzIGxldmVsLiBZb3UgY2FuIHZpZXcgW2FsbCA2MCBwbG90cyBoZXJlXShodHRwczovL2Rhdmlkcm1jY295LmdpdGh1Yi5pby9EYXZpZFJNY0NveS5pby9UWF9lZF9hbGwuaHRtbCkuIFlvdSBjYW4gc2VlIGEgbWFwIG9mIHRoZSBbVGV4YXMgRVNDIFJlZ2lvbnMgaGVyZV0oaHR0cHM6Ly9ycHRzdnIxLnRlYS50ZXhhcy5nb3YvcGVyZnJlcG9ydC9zbmFwc2hvdC8yMDIwL3JlZ2lvbi5zcmNoLmh0bWwpLiAgIA0KDQpCZWxvdyBhcmUgdGhlIHBsb3RzIGZvciB0aGUgbGFyZ2VzdCAoUmVnaW9uIDQ6IEhvdXN0b24gYXJlYSkgYW5kIGEgbWlkLXNpemVkIChSZWdpb24gMTk6IEVsIFBhc28gYXJlYSkgc3Vic2V0LiBUaGVzZSBleHBsb3JhdG9yeSBwbG90cyBkZW1vbnN0cmF0ZSB0aGUgc3Ryb25nIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBvdmVydHksIHJhY2UvZXRobmljaXR5LCBhbmQgZWR1Y2F0aW9uYWwgb3V0Y29tZXMuIEJldHdlZW4gc2Nob29scywgYW5kIGdlbmVyYWxseSB3aXRoaW4gc2Nob29scywgd2hpdGUgc3R1ZGVudHMgdGVuZCB0byBoYXZlIGhpZ2hlciByYXRlcyBvZiBtZWV0aW5nIGdyYWRlLWxldmVsIGV4cGVjdGF0aW9ucy4gVGhlIGZ1bGwgc2FtcGxlIHBsb3RzIHJldmVhbCB0aGF0IHJhY2UgaGFzIGEgcGVyc2lzdGVudCBkaWZmZXJlbmNlIGxldmVsIChvciBncm93dGggaW50ZXJjZXB0KSByZWdhcmRsZXNzIG9mIHRoZSBhZ2UgZ3JvdXAgb2YgdGhlIHN0dWRlbnRzLiBIb3dldmVyIHRoZSB0cmVuZCBhY3Jvc3MgcG92ZXJ0eSBsZXZlbHMgKG9yIChuZWdhdGl2ZSkgZ3Jvd3RoIHNsb3BlKSBpcyBpbmNyZWFzaW5nbHkgbm9uLWxpbmVhciBhcyBzdHVkZW50cyBhZ2UgaW50byBoaWdoIHNjaG9vbC4gVGhpcyBpcyBldmlkZW50IGluIHRoZSBpbmNyZWFzaW5nbHkgc3RlZXAgTG9lc3MgY3VydmVzIG9uIHRoZSBleHRyZW1pdGllcyBvZiB0aGUgeC1heGlzIGZvciBoaWdoIHNjaG9vbCBjYW1wdXNlcywgY29tcGFyZWQgd2l0aCBvdGhlciBjYW1wdXNlcywgYXMgd2VsbCBhcyBqdW5pb3IgaGlnaCBjYW1wdXNlcyBjb21wYXJlZCB3aXRoIGVsZW1lbnRhcnkgY2FtcHVzZXMuIFRoaXMgYW5hbHlzaXMgaXMgZXhwbG9yYXRvcnkgYW5kIGRvZXMgbm90IG1ha2UgY2F1c2FsIGNsYWltcy4gSXQgaXMgaW1wb3J0YW50IHRvIHJlbWVtYmVyIHRoYXQgSSBoYXZlIG5vdCBtYWRlIHN1ZmZpY2llbnQgZWZmb3J0cyBhdCBlbGltaW5hdGluZyBlbmRvZ2VuZWl0eSB0byBtYWtlIGNhdXNhbCBjbGFpbXMuIE9uZSBjbGVhciB0YWtlYXdheSwgdGhvdWdoLCBpcyB0aGF0IGZ1cnRoZXIgd29yayB3b3VsZCBuZWVkIHRvIHJlY29nbml6ZSB0aGlzIG5vbi1saW5lYXJpdHkgaW4gYW55IGFuYWx5c2lzIHVzaW5nIHRoaXMgZGF0YS4gT3RoZXJ3aXNlLCByZWxhdGlvbnNoaXBzIGFtb25nIHRoZXNlIGZhY3RvcnMgbWlnaHQgYmUgb3Zlcmxvb2tlZC4gIA0KDQpPbmUgc2FsaWVudCBmYWN0IHRoYXQgZW1lcmdlcyBmcm9tIHRoaXMgZGF0YSBpcyB0aGUgcGVyc2lzdGVudCBhbmQgcGVydmFzaXZlIGluZXF1YWxpdHkgaW4gcGVyZm9ybWFuY2UgYW1vbmcgc3R1ZGVudHMgd2l0aCBkaWZmZXJpbmcgcmFjaWFsL2V0aG5pYyBiYWNrZ3JvdW5kcy4gSXQgaXMgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IGFuIGltcG9ydGFudCBzb3VyY2Ugb2YgZW5kb2dlbmVpdHkgcmVtYWlucyBpbiB0aGlzIGRhdGEsIGR1ZSB0byB0aGUgZmFjdCB0aGF0IGBFY28uIERpc2FkdmFudGFnZWQgKCUpYCBpcyBhIGNhbXB1cy1sZXZlbCB2YXJpYWJsZSBhbmQgbm90IGEgYFN0dWRlbnQgR3JvdXBgIHZhcmlhYmxlLiBXaXRoaW4gYSBnaXZlbiBjYW1wdXMsIGlmIHRoZSB3aGl0ZSBzdHVkZW50cyBhcmUgbGVzcyBsaWtlbHkgdG8gYmUgYW1vbmcgdGhvc2Ugc3R1ZGVudHMgd2hvIGFyZSBlY29ub21pY2FsbHkgZGlzYWR2YW50YWdlZCwgdGhlIGNhbXB1cy1sZXZlbCBwb3ZlcnR5IGxvc2VzIHRoZSBhYmlsaXR5IHRvIGV4cGxhaW4gdGhlc2Ugb3V0Y29tZXMuIFdpdGggYWNjZXNzIHRvIHN0dWRlbnQtbGV2ZWwgZGF0YSB3aXRoaW4gY2FtcHVzZXMsIHRoaXMgYW5hbHlzaXMgY291bGQgZ28gbXVjaCBmdXJ0aGVyLiBSZWdhcmRsZXNzLCBpdCBpcyBjbGVhciB0aGF0IHRoZSBwdWJsaWMgZWR1Y2F0aW9uIHN5c3RlbSBpcyB1bmFibGUgdG8gY29tcGVuc2F0ZSBmb3IgdGhlIGhpc3RvcmljYWwgZGlzYWR2YW50YWdlcyBmYWNlZCBieSByYWNpYWwvZXRobmljIG1pbm9yaXRpZXMsIGFzIGlzIGEgd2VsbCBrbm93biBwcm9ibGVtIGFjcm9zcyB0aGUgVW5pdGVkIFN0YXRlcy4NCmBgYHtyLCByZXN1bHRzID0gImFzaXMiLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0NCiMgSG91c3RvbjogUmVnaW9uIDQNCmh0bWx0b29sczo6dGFnTGlzdChsaXN0KGdnX2hzW1s0XV0sIGdnX2poc1tbNF1dLCBnZ19lc1tbNF1dKSkNCiMgRWwgUGFzbzogUmVnaW9uIDE5DQpodG1sdG9vbHM6OnRhZ0xpc3QobGlzdChnZ19oc1tbMTldXSwgZ2dfamhzW1sxOV1dLCBnZ19lc1tbMTldXSkpDQojIEZ1bGwgU2FtcGxlcw0KaHRtbHRvb2xzOjp0YWdMaXN0KGxpc3QoZnNbWzFdXSwgZnNbWzJdXSwgZnNbWzNdXSkpDQoNCmBgYA0KDQojIE1vZGVyYXRpb24gb2YgUmVsYXRpb25zaGlwcyBieSBQcml2YXRlIFNjaG9vbCBBdHRlbmRhbmNlIFJhdGVzDQoNCiMjIFByaXZhdGUgU2Nob29scyBEYXRhIDIwMTggLSAyMDE5DQoNCkhvdyBkb2VzIHByaXZhdGUgc2Nob29sIGF0dGVuZGFuY2UgbW9kZXJhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJhY2UvZXRobmljaXR5LCBwb3ZlcnR5IGxldmVscywgYW5kIGVkdWNhdGlvbmFsIG91dGNvbWVzPyBJIGRvd25sb2FkZWQgdGhlIDIwMTkgcHJpdmF0ZSBzY2hvb2wgZW5yb2xsbWVudCBkYXRhIGZyb20gdGhlIFtUZXhhcyBQcml2YXRlIFNjaG9vbCBBY2NyZWRpdGF0aW9uIENvbW1pc3Npb24gc2l0ZV0oaHR0cDovL3d3dy50ZXBzYWMub3JnL2FwcC9pbmRleC5odG1sIy9zZWFyY2gvYXJjaGl2ZSkuIEkgZ3JvdXBlZCB0aGUgZGF0YSBieSBbcmVnaW9uXShodHRwczovL3JwdHN2cjEudGVhLnRleGFzLmdvdi9wZXJmcmVwb3J0L3NuYXBzaG90LzIwMjAvcmVnaW9uLnNyY2guaHRtbCkgYW5kIGNvbWJpbmVkIGl0IHdpdGggdGhlIHB1YmxpYyBzY2hvb2wtbGV2ZWwgZGF0YSBmcm9tIHRoZSBbVGV4YXMgRWR1Y2F0aW9uIEFnZW5jeSdzIGRhdGFdKGh0dHBzOi8vcnB0c3ZyMS50ZWEudGV4YXMuZ292L3BlcmZyZXBvcnQvYWNjb3VudC92YS92YV9jb3JyZWxhdGUuaHRtbCkgdXNlZCBhYm92ZS4gSSB3YW50IHRvIGV4cGxvcmUgd2hldGhlciBkaWZmZXJlbnQgcGF0dGVybnMgZW1lcmdlIGluIHRoZXNlIHJlbGF0aW9uc2hpcHMgd2hlbiB0aGUgZGF0YSBpcyBzdWJzZXQgdG8gaW5jbHVkZSBzbWFsbCByZWdpb25zIHdpdGggbG93IGFuZCBoaWdoIHJhdGVzIG9mIHByaXZhdGUgc3R1ZGVudCBhdHRlbmRhbmNlLg0KDQpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUZ9DQojIExvYWQgMjAxOSBwcml2YXRlIHNjaG9vbCBkYXRhIGFuZCBGaWx0ZXIgb3V0IHByZS1LIGFuZCBjbG9zZWQgc2Nob29scw0KcHJpdmF0ZSA8LSAgcmVhZF9leGNlbCgiZG9jcy9wcml2YXRlIHNjaG9vbHMgMjAxOC0yMDE5Lnhsc3giKQ0KcHJpdmF0ZSA8LXByaXZhdGUgJT4lDQogIGZpbHRlcighYEdyYWRlIEhpZ2hgICVpbiUgYygiUHJlLUsiLCAiRWFybHkgRWR1Y2F0aW9uIikgJiBDbG9zZWQgPT0gRkFMU0UpICU+JQ0KICB0cmFuc211dGUoDQogICAgZGlzdHJpY3QgPSBhcy5udW1lcmljKGBEaXN0cmljdCBOdW1iZXJgKSwNCiAgICBjb3VudHkgPSBhcy5udW1lcmljKGBDb3VudHkgTnVtYmVyYCksDQogICAgcmVnaW9uID0gYXMubnVtZXJpYyhgUmVnaW9uIE5hbWVgKSwNCiAgICBlbnJvbGxtZW50X3ByaXZhdGUgPSBhcy5udW1lcmljKEVucm9sbG1lbnQpKQ0KIyBBZ2dyZWdhdGUgZW5yb2xsbWVudCBieSByZWdpb24NCnByaXZhdGVfcmVnaW9uIDwtIHByaXZhdGUgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBwcml2YXRlX2Vucm9sbG1lbnRfcmVnaW9uID0gc3VtKGVucm9sbG1lbnRfcHJpdmF0ZSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHVuZ3JvdXAoKQ0KIyBBZ2dyZWdhdGUgcHVibGljIHNjaG9vbCBlbnJvbGxtZW50IGJ5IHJlZ2lvbg0KcHVibGljX3JlZ2lvbiA8LSBzY2hvb2wgJT4lDQogIGxlZnRfam9pbihjb2RlcywgYnkgPSAiY2lkIikgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBwdWJsaWNfZW5yb2xsbWVudF9yZWdpb24gPSBzdW0oZW5yb2xsbWVudF9wdWJsaWMsIG5hLnJtID0gVFJVRSkpICU+JQ0KICB1bmdyb3VwKCkNCiNKb2luIGFuZCBjcmVhdGUgdmFyaWFibGVzIGFuZCBmYWN0b3IgbGFiZWxzDQpyZWdpb24gPC0gbGVmdF9qb2luKHByaXZhdGVfcmVnaW9uLCBwdWJsaWNfcmVnaW9uLCBieSA9ICJyZWdpb24iKSANCnJlZ2lvbiA8LSByZWdpb24gJT4lDQogIG11dGF0ZSgNCiAgICBgVG90YWwgRW5yb2xsbWVudGAgPSBwcml2YXRlX2Vucm9sbG1lbnRfcmVnaW9uICsgcHVibGljX2Vucm9sbG1lbnRfcmVnaW9uLA0KICAgIGBQcml2YXRlIFN0dWRlbnRzIGluIFJlZ2lvbiAoJSlgID0gMTAwICogKCBwcml2YXRlX2Vucm9sbG1lbnRfcmVnaW9uIC8gYFRvdGFsIEVucm9sbG1lbnRgKSwNCiAgICBSZWdpb25fbGFiID0gcGFzdGUoIlJlZ2lvbiIsIHJlZ2lvbiwgc2VwID0gIiAiKSwNCiAgICBSZWdpb24gPSBmYWN0b3IocmVnaW9uLCBvcmRlcmVkID0gVFJVRSwgbGFiZWxzID0gUmVnaW9uX2xhYiksDQogICAgYFByaXZhdGUgU2Nob29sIFJhdGVzYCA9IGlmZWxzZSgNCiAgICAgIHJlZ2lvbiAlaW4lIGMoMywgMTgsIDIsIDE5LCA2KSwgIkhpZ2ggYW5kIFNtYWxsIFBvcC4iLCBpZmVsc2UoDQogICAgICAgIHJlZ2lvbiAlaW4lIGMoMTIsIDE1LCAxNCwgOCwgMTYpLCAiTG93IGFuZCBTbWFsbCBQb3AuIiwgIk1pZGRsZSBvciBMYXJnZSBQb3AuIikpKSAlPiUNCiAgYXJyYW5nZShgUHJpdmF0ZSBTdHVkZW50cyBpbiBSZWdpb24gKCUpYCkgJT4lDQogIG11dGF0ZSgNCiAgICBSZWdpb25fbGFiID0gcGFzdGUoIlJlZ2lvbiIsIHJlZ2lvbiwgc2VwID0gIiAiKSwNCiAgICBgUmVnaW9uIE9yZGVyZWRgID0gZmFjdG9yKHJlZ2lvbiwgbGV2ZWxzID0gcmVnaW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHBhc3RlKCJSZWdpb24iLCByZWdpb24sIHNlcCA9ICIgIikpKQ0KYGBgDQoNCiMjIFZpc3VhbGl6aW5nIFB1YmxpYyBhbmQgUHJpdmF0ZSBTY2hvb2xzDQpJbiBvcmRlciB0byBtYWtlIHRoZSBjb21wYXJpc29uIG1vcmUgcmVhc29uYWJsZSwgSSBmb2N1cyBvbiBFU0MgcmVnaW9ucyB3aXRoIHNpbWlsYXIgYFRvdGFsIEVucm9sbG1lbnRgIHRoYXQgaGF2ZSBsb3cgYW5kIGhpZ2ggcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZSBpbiB0aGUgcmVnaW9uLiBUaHVzLCB0aGUgZ3JvdXBzIG9mIHNjaG9vbHMgSSBjb21wYXJlIGxhdGVyIGNvbWUgZnJvbSB0aGUgcmVkIHJlZ2lvbnMgKHNtYWxsIHJlZ2lvbnMgd2l0aCByYXRlcyBvZiBwcml2YXRlIHNjaG9vbCBhdHRlbmRhbmNlKSBhbmQgdGhlIGdyZWVuIHJlZ2lvbnMgKHNtYWxsIHJlZ2lvbnMgd2l0aCBsb3cgcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZSkuIEkgc2hvdWxkIG5vdGUgdGhhdCB0aGlzIGVmZm9ydCB0byBjaG9vc2UgY29tcGFyYWJsZSBncm91cHMgb2YgcmVnaW9ucyBkb2VzIG5vdCBzdWZmaWNpZW50bHkgZWxpbWluYXRlIGVuZG9nZW5laXR5IHRvIGNvbnNpZGVyIGFueSBvdXRjb21lcyBjYXVzYWwuIFRoaXMgaXMgYW4gZXhwbG9yYXRvcnkgYW5hbHlzaXMuDQpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnfQ0KDQojIFNjYXR0ZXIgYW5kIGJhciBwbG90cw0KcyA8LSBnZ3Bsb3QocmVnaW9uLCANCiAgICAgICAgICAgIGFlcyh4ID0gbG9nKGBUb3RhbCBFbnJvbGxtZW50YCksIA0KICAgICAgICAgICAgICAgIHkgPSBgUHJpdmF0ZSBTdHVkZW50cyBpbiBSZWdpb24gKCUpYCwgDQogICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpb24sIA0KICAgICAgICAgICAgICAgIGNvbG9yID0gYFByaXZhdGUgU2Nob29sIFJhdGVzYCkpICsNCiAgZ2VvbV90ZXh0KHNpemUgPSA1KSArDQogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSANCmIgPC0gZ2dwbG90KHJlZ2lvbiwgDQogICAgICAgICAgICBhZXMoeCA9IGBQcml2YXRlIFN0dWRlbnRzIGluIFJlZ2lvbiAoJSlgLCANCiAgICAgICAgICAgICAgICB5ID0gYFJlZ2lvbiBPcmRlcmVkYCwgDQogICAgICAgICAgICAgICAgZmlsbCA9IGBQcml2YXRlIFNjaG9vbCBSYXRlc2ApKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmdnYXJyYW5nZShzLCBiLCBucm93ID0gMSwgY29tbW9uLmxlZ2VuZCA9IFRSVUUsIGxlZ2VuZCA9ICJib3R0b20iKQ0KYGBgDQoNCiMjIEVkdWNhdGlvbiBPdXRjb21lcyBieSBMb3cgYW5kIEhpZ2ggUmF0ZXMgb2YgUmVnaW9uYWwgUHJpdmF0ZSBTY2hvb2wgQXR0ZW5kYW5jZQ0KVGhlIG1vc3Qgbm90YWJsZSBkaWZmZXJlbmNlIGhlcmUgaXMgdGhhdCBleHRyZW1lbHkgcHJpdmlsZWdlZCBvciBkaXNhZHZhbnRhZ2VkIHNjaG9vbHMgaGF2ZSBtb3JlIGV4dHJlbWUgdmFsdWVzIGluIHJlZ2lvbnMgd2hlcmUgdGhlcmUgYXJlIGhpZ2ggcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZS4gVGhpcyBpcyBhcHBhcmVudCBpbiB0aGUgcmVsYXRpdmVseSBzdGVlcCBjdXJ2ZXMgb24gdGhlIGV4dHJlbWl0aWVzIG9mIHRoZSBzZWNvbmQgcGxvdCAoIkhpZ2hlciBSYXRlcy4uLiIpLiBXaGlsZSB0aGlzIGRhdGEgY2Fubm90IHJldmVhbCBjYXVzYWwgcmVsYXRpb25zaGlwcywgd2UgY2FuIHNheSB0aGF0IHJlZ2lvbnMgd2l0aCBoaWdoZXIgcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZSBoYXZlIGdyZWF0ZXIgaW5lcXVhbGl0eSBhbW9uZyBzdHVkZW50cyBhY3Jvc3Mgc3R1ZGVudHMgZnJvbSBtb3JlIGFuZCBsZXNzIHByaXZpbGVnZWQgY2FtcHVzZXMuIFdlIGNhbiBmdXJ0aGVyIGV4YW1pbmUgdGhpcyBieSBkaXJlY3RseSBjb21wYXJpbmcgcmFjaWFsL2V0aG5pYyBncm91cHMuDQoNCmBgYHtyLCByZXN1bHRzID0gImFzaXMiLCB3YXJuaW5nPUYsIG1lc3NhZ2U9RiwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZSd9DQojIExvdyBhbmQgaGlnaCBwcml2YXRlIHNjaG9vbCBhdHRlbmRhbmNlDQoNCnBzIDwtIGxpc3QoKQ0KdCA8LSAgZ2dwbG90KGRhdGEgPSBjYW1wdXMgJT4lIGZpbHRlcihgQ2FtcHVzIFR5cGVgID09ICJIaWdoIFNjaG9vbCIgJiByZWdpb24gJWluJSBjKDEyLCAxNSwgMTQsIDgsIDE2KSksIA0KICAgICAgICAgYWVzKGxhYmVsID0gYENhbXB1c2AsIGxhYmVsMiA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgeCA9IGBFY28uIERpc2FkdmFudGFnZWQgKCUpYCwgeSA9IGBNZWV0cyBHcmFkZSBMZXZlbCAoJSlgKSkgKw0KICBnZW9tX2xpbmUoYWxwaGEgPSAuMywgYWVzKGdyb3VwID0gY2lkKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjQsIGFlcyhzaXplID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCBjb2xvciA9IGBTdHVkZW50IEdyb3VwYCkpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDIsIG1ldGhvZCA9IGxvZXNzLCBjb2xvciA9ICJibGFjayIsIHNlID0gRkFMU0UsIGFlcyhncm91cCA9IGBTdHVkZW50IEdyb3VwYCwgd2VpZ2h0ID0gbG5fbWVldHNfZGVub20pKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAxLjgsIG1ldGhvZCA9IGxvZXNzLCBzZSA9IEZBTFNFLCBhZXMoY29sb3IgPSBgU3R1ZGVudCBHcm91cGAsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBndWlkZXMoc2l6ZSA9IEZBTFNFKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIHhsaW0oYygwLDEwMCkpICsgeWxpbShjKDAsMTAwKSkgKyANCiAgICBnZ3RpdGxlKCJIaWdoIFNjaG9vbHM6IFNtYWxsIFJlZ2lvbnMgd2l0aCBMb3dlciBSYXRlcyBvZiBQcml2YXRlIFNjaG9vbCBBdHRlbmRhbmNlIikNCnBzW1sxXV0gPC0gZ2dwbG90bHkodCwgdG9vbHRpcCA9IGMoImxhYmVsIiwgImxhYmVsMiIsICJ4IiwgInkiKSkNCg0KdCA8LSAgZ2dwbG90KGRhdGEgPSBjYW1wdXMgJT4lIGZpbHRlcihgQ2FtcHVzIFR5cGVgID09ICJIaWdoIFNjaG9vbCIgJiByZWdpb24gJWluJSBjKDMsIDE4LCAyLCAxOSwgNikpLCANCiAgICAgICAgIGFlcyhsYWJlbCA9IGBDYW1wdXNgLCBsYWJlbDIgPSBgU2l6ZSBvZiBTdHVkZW50IEdyb3VwIGF0IFNjaG9vbGAsIHggPSBgRWNvLiBEaXNhZHZhbnRhZ2VkICglKWAsIHkgPSBgTWVldHMgR3JhZGUgTGV2ZWwgKCUpYCkpICsNCiAgZ2VvbV9saW5lKGFscGhhID0gLjMsIGFlcyhncm91cCA9IGNpZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40LCBhZXMoc2l6ZSA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgY29sb3IgPSBgU3R1ZGVudCBHcm91cGApKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyLCBtZXRob2QgPSBsb2VzcywgY29sb3IgPSAiYmxhY2siLCBzZSA9IEZBTFNFLCBhZXMoZ3JvdXAgPSBgU3R1ZGVudCBHcm91cGAsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMS44LCBtZXRob2QgPSBsb2Vzcywgc2UgPSBGQUxTRSwgYWVzKGNvbG9yID0gYFN0dWRlbnQgR3JvdXBgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICB4bGltKGMoMCwxMDApKSArIHlsaW0oYygwLDEwMCkpICsgDQogICAgZ2d0aXRsZSgiSGlnaCBTY2hvb2xzOiBTbWFsbCBSZWdpb25zIHdpdGggSGlnaGVyIFJhdGVzIG9mIFByaXZhdGUgU2Nob29sIEF0dGVuZGFuY2UiKQ0KcHNbWzJdXSA8LSBnZ3Bsb3RseSh0LCB0b29sdGlwID0gYygibGFiZWwiLCAibGFiZWwyIiwgIngiLCAieSIpKQ0KDQpodG1sdG9vbHM6OnRhZ0xpc3QobGlzdChwc1tbMV1dLCBwc1tbMl1dKSkNCg0KYGBgDQoNCg0KDQojIyBSYWNpYWwvRXRobmljIEdyb3VwIENvbXBhcmlzb24gYnkgTG93IGFuZCBIaWdoIFJhdGVzIG9mIFJlZ2lvbmFsIFByaXZhdGUgU2Nob29sIEF0dGVuZGFuY2UNCg0KVGhlIGZpbmFsIGV4cGxvcmF0b3J5IGFuYWx5c2lzIHRha2VzIHRoZSBzYW1lIGRhdGEgYW5kIHNsaWNlcyBpdCBpbnRvIHRoZSB0aHJlZSByYWNpYWwvZXRobmljIGdyb3VwcyBmb3IgdGhlIGxvd2VyIGFuZCBoaWdoZXIgcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZSBpbiByZWdpb24uIFRoZXJlIGFyZSBhdCBsZWFzdCB0d28gZGVzY3JpcHRpdmUgdGFrZWF3YXlzIGhlcmU6DQoNCjEuIFNjaG9vbHMgaW4gcmVnaW9ucyB3aXRoIGxvd2VyIHByaXZhdGUgc2Nob29sIGF0dGVuZGFuY2UgcmF0ZXMgdGVuZCB0byBoYXZlIHNsaWdodGx5IGxvd2VyIHJhdGVzIG9mIG1lZXRpbmcgdGhlaXIgZ3JhZGUgbGV2ZWxzLCBmb3IgYSBnaXZlbiBjYW1wdXMtbGV2ZWwgcG92ZXJ0eSBhbmQgcmFjaWFsL2V0aG5pYyBncm91cC4gVGhpcyBpcyB2aXNpYmxlIGluIHRoZSBmYWN0IHRoYXQgdGhlIHJlZCBsaW5lcyAoY2FtcHVzZXMgaW4gcmVnaW9ucyB3aXRoIGhpZ2hlciByYXRlcyBvZiBwcml2YXRlIGF0dGVuZGFuY2UpIGFyZSBnZW5lcmFsbHkgc2xpZ2h0bHkgaGlnaGVyIHRoYW4gdGhlIHRlYWwgbGluZSBmb3IgZWFjaCByYWNpYWwvZXRobmljIGdyb3VwIHBsb3QuICAgDQoyLiBTY2hvb2xzIGluIHJlZ2lvbnMgd2l0aCBoaWdoZXIgcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2wgYXR0ZW5kYW5jZSBoYXZlIGhpZ2hlciBpbmVxdWFsaXR5IGluIHBlcmZvcm1hbmNlIGFtb25nIGNhbXB1c2VzIHdpdGhpbiBlYWNoIHJhY2lhbC9ldGhuaWMgZ3JvdXAuIFRoYXQgaXMgdG8gc2F5IHRoYXQgZXh0cmVtZWx5IGRpc2FkdmFudGFnZWQgY2FtcHVzZXMgaW4gcmVnaW9ucyB3aXRoIG1hbnkgcHJpdmF0ZSBzY2hvb2xzIHBlcmZvcm0gY29tcGFyYXRpdmVseSB3b3JzZSB0aGFuIG90aGVyIGNhbXB1c2VzIGluIHRoZSBzYW1lIHN1YnNldCBvZiBkYXRhIGFuZCB0aGUgc2FtZSBpcyB0cnVlIGZvciBleHRyZW1lbHkgcHJpdmlsZWdlZCBjYW1wdXNlcyBwZXJmb3JtaW5nIG11Y2ggYmV0dGVyIGluIHJlZ2lvbnMgd2l0aCBoaWdoZXIgcmF0ZXMgb2YgcHJpdmF0ZSBzY2hvb2xzLiANCg0KYGBge3IsIHJlc3VsdHMgPSAiYXNpcyIsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQ0KY2FtcHVzIDwtIGNhbXB1cyAlPiUNCiAgbXV0YXRlKA0KICAgIGBQcml2YXRlIFNjaG9vbCBSYXRlc2AgPSBpZmVsc2UoDQogICAgICByZWdpb24gJWluJSBjKDMsIDE4LCAyLCAxOSwgNiksICJIaWdoIGFuZCBTbWFsbCBQb3AuIiwgaWZlbHNlKA0KICAgICAgICByZWdpb24gJWluJSBjKDEyLCAxNSwgMTQsIDgsIDE2KSwgIkxvdyBhbmQgU21hbGwgUG9wLiIsICJNaWRkbGUgb3IgTGFyZ2UgUG9wLiIpKSkgDQogIA0KDQp0IDwtICBnZ3Bsb3QoZGF0YSA9IGNhbXB1cyAlPiUgZmlsdGVyKGBDYW1wdXMgVHlwZWAgPT0gIkhpZ2ggU2Nob29sIiAmIHJlZ2lvbiAlaW4lIGMoMTIsIDE1LCAxNCwgOCwgMTYsIDMsIDE4LCAyLCAxOSwgNikgJiBgU3R1ZGVudCBHcm91cGAgPT0gIkFmcmljYW4gQW1lcmljYW4iKSwgDQogICAgICAgICBhZXMobGFiZWwgPSBgQ2FtcHVzYCwgbGFiZWwyID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCB4ID0gYEVjby4gRGlzYWR2YW50YWdlZCAoJSlgLCB5ID0gYE1lZXRzIEdyYWRlIExldmVsICglKWApKSArDQogICNnZW9tX2xpbmUoYWxwaGEgPSAuMywgYWVzKGdyb3VwID0gY2lkKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjQsIGFlcyhzaXplID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCBjb2xvciA9IGBQcml2YXRlIFNjaG9vbCBSYXRlc2ApKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyLCBtZXRob2QgPSBsb2VzcywgY29sb3IgPSAiYmxhY2siLCBzZSA9IEZBTFNFLCBhZXMoZ3JvdXAgPSBgUHJpdmF0ZSBTY2hvb2wgUmF0ZXNgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDEuOCwgbWV0aG9kID0gbG9lc3MsIHNlID0gRkFMU0UsIGFlcyhjb2xvciA9IGBQcml2YXRlIFNjaG9vbCBSYXRlc2AsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBndWlkZXMoc2l6ZSA9IEZBTFNFKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIHhsaW0oYygwLDEwMCkpICsgeWxpbShjKDAsMTAwKSkgKyANCiAgICBnZ3RpdGxlKCJBZnJpY2FuIEFtZXJpY2FuIFN0dWRlbnRzOiBIaWdoIFNjaG9vbHMgaW4gU21hbGwgUmVnaW9ucyB3aXRoIExvd2VyIFJhdGVzIG9mIFByaXZhdGUgU2Nob29sIEF0dGVuZGFuY2UiKQ0KcHNbWzNdXSA8LSBnZ3Bsb3RseSh0LCB0b29sdGlwID0gYygibGFiZWwiLCAibGFiZWwyIiwgIngiLCAieSIpKQ0KDQp0IDwtICBnZ3Bsb3QoZGF0YSA9IGNhbXB1cyAlPiUgZmlsdGVyKGBDYW1wdXMgVHlwZWAgPT0gIkhpZ2ggU2Nob29sIiAmIHJlZ2lvbiAlaW4lIGMoMTIsIDE1LCAxNCwgOCwgMTYsIDMsIDE4LCAyLCAxOSwgNikgJiBgU3R1ZGVudCBHcm91cGAgPT0gIkhpc3BhbmljIiksIA0KICAgICAgICAgYWVzKGxhYmVsID0gYENhbXB1c2AsIGxhYmVsMiA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgeCA9IGBFY28uIERpc2FkdmFudGFnZWQgKCUpYCwgeSA9IGBNZWV0cyBHcmFkZSBMZXZlbCAoJSlgKSkgKw0KICAjZ2VvbV9saW5lKGFscGhhID0gLjMsIGFlcyhncm91cCA9IGNpZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40LCBhZXMoc2l6ZSA9IGBTaXplIG9mIFN0dWRlbnQgR3JvdXAgYXQgU2Nob29sYCwgY29sb3IgPSBgUHJpdmF0ZSBTY2hvb2wgUmF0ZXNgKSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMiwgbWV0aG9kID0gbG9lc3MsIGNvbG9yID0gImJsYWNrIiwgc2UgPSBGQUxTRSwgYWVzKGdyb3VwID0gYFByaXZhdGUgU2Nob29sIFJhdGVzYCwgd2VpZ2h0ID0gbG5fbWVldHNfZGVub20pKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAxLjgsIG1ldGhvZCA9IGxvZXNzLCBzZSA9IEZBTFNFLCBhZXMoY29sb3IgPSBgUHJpdmF0ZSBTY2hvb2wgUmF0ZXNgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ3VpZGVzKHNpemUgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICB4bGltKGMoMCwxMDApKSArIHlsaW0oYygwLDEwMCkpICsgDQogICAgZ2d0aXRsZSgiSGlzcGFuaWMgU3R1ZGVudHM6IEhpZ2ggU2Nob29scyBpbiBTbWFsbCBSZWdpb25zIHdpdGggTG93ZXIgUmF0ZXMgb2YgUHJpdmF0ZSBTY2hvb2wgQXR0ZW5kYW5jZSIpDQpwc1tbNF1dIDwtIGdncGxvdGx5KHQsIHRvb2x0aXAgPSBjKCJsYWJlbCIsICJsYWJlbDIiLCAieCIsICJ5IikpDQoNCnQgPC0gIGdncGxvdChkYXRhID0gY2FtcHVzICU+JSBmaWx0ZXIoYENhbXB1cyBUeXBlYCA9PSAiSGlnaCBTY2hvb2wiICYgcmVnaW9uICVpbiUgYygxMiwgMTUsIDE0LCA4LCAxNiwgMywgMTgsIDIsIDE5LCA2KSAmIGBTdHVkZW50IEdyb3VwYCA9PSAiV2hpdGUiKSwgDQogICAgICAgICBhZXMobGFiZWwgPSBgQ2FtcHVzYCwgbGFiZWwyID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCB4ID0gYEVjby4gRGlzYWR2YW50YWdlZCAoJSlgLCB5ID0gYE1lZXRzIEdyYWRlIExldmVsICglKWApKSArDQogICNnZW9tX2xpbmUoYWxwaGEgPSAuMywgYWVzKGdyb3VwID0gY2lkKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjQsIGFlcyhzaXplID0gYFNpemUgb2YgU3R1ZGVudCBHcm91cCBhdCBTY2hvb2xgLCBjb2xvciA9IGBQcml2YXRlIFNjaG9vbCBSYXRlc2ApKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAyLCBtZXRob2QgPSBsb2VzcywgY29sb3IgPSAiYmxhY2siLCBzZSA9IEZBTFNFLCBhZXMoZ3JvdXAgPSBgUHJpdmF0ZSBTY2hvb2wgUmF0ZXNgLCB3ZWlnaHQgPSBsbl9tZWV0c19kZW5vbSkpICsNCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IDEuOCwgbWV0aG9kID0gbG9lc3MsIHNlID0gRkFMU0UsIGFlcyhjb2xvciA9IGBQcml2YXRlIFNjaG9vbCBSYXRlc2AsIHdlaWdodCA9IGxuX21lZXRzX2Rlbm9tKSkgKw0KICBndWlkZXMoc2l6ZSA9IEZBTFNFKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIHhsaW0oYygwLDEwMCkpICsgeWxpbShjKDAsMTAwKSkgKyANCiAgICBnZ3RpdGxlKCJXaGl0ZSBTdHVkZW50czogSGlnaCBTY2hvb2xzIGluIFNtYWxsIFJlZ2lvbnMgd2l0aCBMb3dlciBSYXRlcyBvZiBQcml2YXRlIFNjaG9vbCBBdHRlbmRhbmNlIikNCnBzW1s1XV0gPC0gZ2dwbG90bHkodCwgdG9vbHRpcCA9IGMoImxhYmVsIiwgImxhYmVsMiIsICJ4IiwgInkiKSkNCg0KaHRtbHRvb2xzOjp0YWdMaXN0KGxpc3QocHNbWzNdXSwgcHNbWzRdXSwgcHNbWzVdXSkpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==